├── .gitignore
├── CMakeLists.txt
├── README.md
├── cmake
├── DragNDrop
│ ├── DS_Store
│ └── background.png
└── MacOSXBundleInfo.plist.in
├── doc
└── screenshot_macos.png
├── res
├── HLMV_128x128.png
├── HLMV_256x256.png
├── Icon.icns
└── makeicns.sh
└── src
├── c
├── hlmv_application.cpp
├── hlmv_application.h
├── hlmv_body.cpp
├── hlmv_cso_model.cpp
├── hlmv_display.cpp
├── hlmv_event.cpp
├── hlmv_file.cpp
├── hlmv_origin.cpp
├── hlmv_seq.cpp
├── hlmv_tab.cpp
├── hlmv_texture.cpp
├── qt_image.cpp
└── qt_image.h
├── gl.h
├── m
├── IceKey.cpp
├── IceKey.hpp
├── StudioModel.h
├── TGAlib.cpp
├── TGAlib.h
├── ViewerSettings.cpp
├── ViewerSettings.h
├── bmpread.cpp
├── bmpread.h
├── gl_draw.cpp
├── gl_draw.h
├── mathlib.c
├── mathlib.h
├── mod_decryptor.cpp
├── mod_decryptor.h
├── studio.h
├── studio_render.cpp
└── studio_utils.cpp
├── main.cpp
└── v
├── hlmv.cpp
├── hlmv.h
├── hlmv.qrc
├── hlmv.ui
├── myopenglwidget.cpp
├── myopenglwidget.h
└── platform
├── macos
├── MacWindow.mm
└── Touchbar.mm
└── win32
└── AeroWindow.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries
2 | *.o
3 | *.so
4 | *.a
5 | *.framework
6 |
7 | # Other
8 | *.save
9 | # Qt Creator for some reason creates *.user.$version files, so exclude it too
10 | *.user*
11 | *~
12 |
13 | ### Xcode ###
14 | build/
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 | xcuserdata
24 | *.xccheckout
25 | *.moved-aside
26 | DerivedData
27 | *.xcuserstate
28 |
29 |
30 | ### OSX ###
31 | .DS_Store
32 | .AppleDouble
33 | .LSOverride
34 |
35 |
36 | # Thumbnails
37 | ._*
38 |
39 | # Files that might appear on external disk
40 | .Spotlight-V100
41 | .Trashes
42 |
43 | # Directories potentially created on remote AFP share
44 | .AppleDB
45 | .AppleDesktop
46 | Network Trash Folder
47 | Temporary Items
48 | .apdisk
49 |
50 |
51 | ### CMake ###
52 | CMakeCache.txt
53 | CMakeFiles
54 | Makefile
55 | cmake_install.cmake
56 | install_manifest.txt
57 | # makedepend
58 | Makefile.dep
59 | *.bak
60 | ALL_BUILD.*
61 | INSTALL.*
62 | ZERO_CHECK.*
63 |
64 | # Visual Studio
65 | *.obj
66 | *.exp
67 | *.suo
68 | *.sdf
69 | Debug/
70 | Release/
71 | ipch/
72 | *.opensdf
73 |
74 | ## Ignore Visual Studio temporary files, build results, and
75 | ## files generated by popular Visual Studio add-ons.
76 |
77 | # User-specific files
78 | *.suo
79 | *.user
80 | *.userosscache
81 | *.sln.docstates
82 |
83 | # User-specific files (MonoDevelop/Xamarin Studio)
84 | *.userprefs
85 |
86 | # Build results
87 | [Dd]ebug/
88 | [Dd]ebugPublic/
89 | [Rr]elease/
90 | [Rr]eleases/
91 | build/
92 | bld/
93 | [Bb]in/
94 | [Oo]bj/
95 |
96 | # Visual Studio 2015 cache/options directory
97 | .vs/
98 |
99 | # Visual Studio Code Preferences
100 | .vscode/
101 |
102 | # Uncomment if you have tasks that create the project's static files in wwwroot
103 | #wwwroot/
104 |
105 | # MSTest test Results
106 | [Tt]est[Rr]esult*/
107 | [Bb]uild[Ll]og.*
108 |
109 | # NUNIT
110 | *.VisualState.xml
111 | TestResult.xml
112 |
113 | # Build Results of an ATL Project
114 | [Dd]ebugPS/
115 | [Rr]eleasePS/
116 | dlldata.c
117 |
118 | # DNX
119 | project.lock.json
120 | artifacts/
121 |
122 | *_i.c
123 | *_p.c
124 | *_i.h
125 | *.ilk
126 | *.meta
127 | *.obj
128 | *.pch
129 | *.pdb
130 | *.pgc
131 | *.pgd
132 | *.rsp
133 | *.sbr
134 | *.tlb
135 | *.tli
136 | *.tlh
137 | *.tmp
138 | *.tmp_proj
139 | *.log
140 | *.vspscc
141 | *.vssscc
142 | .builds
143 | *.pidb
144 | *.svclog
145 | *.scc
146 |
147 | # Chutzpah Test files
148 | _Chutzpah*
149 |
150 | # Visual C++ cache files
151 | ipch/
152 | *.aps
153 | *.ncb
154 | *.opensdf
155 | *.sdf
156 | *.cachefile
157 |
158 | # Visual Studio profiler
159 | *.psess
160 | *.vsp
161 | *.vspx
162 | *.sap
163 |
164 | # TFS 2012 Local Workspace
165 | $tf/
166 |
167 | # Guidance Automation Toolkit
168 | *.gpState
169 |
170 | # ReSharper is a .NET coding add-in
171 | _ReSharper*/
172 | *.[Rr]e[Ss]harper
173 | *.DotSettings.user
174 |
175 | # JustCode is a .NET coding add-in
176 | .JustCode
177 |
178 | # TeamCity is a build add-in
179 | _TeamCity*
180 |
181 | # DotCover is a Code Coverage Tool
182 | *.dotCover
183 |
184 | # NCrunch
185 | _NCrunch_*
186 | .*crunch*.local.xml
187 | nCrunchTemp_*
188 |
189 | # MightyMoose
190 | *.mm.*
191 | AutoTest.Net/
192 |
193 | # Web workbench (sass)
194 | .sass-cache/
195 |
196 | # Installshield output folder
197 | [Ee]xpress/
198 |
199 | # DocProject is a documentation generator add-in
200 | DocProject/buildhelp/
201 | DocProject/Help/*.HxT
202 | DocProject/Help/*.HxC
203 | DocProject/Help/*.hhc
204 | DocProject/Help/*.hhk
205 | DocProject/Help/*.hhp
206 | DocProject/Help/Html2
207 | DocProject/Help/html
208 |
209 | # Click-Once directory
210 | publish/
211 |
212 | # Publish Web Output
213 | *.[Pp]ublish.xml
214 | *.azurePubxml
215 | *.pubxml
216 | *.publishproj
217 |
218 | # NuGet Packages
219 | *.nupkg
220 | # The packages folder can be ignored because of Package Restore
221 | **/packages/*
222 | # except build/, which is used as an MSBuild target.
223 | !**/packages/build/
224 | # Uncomment if necessary however generally it will be regenerated when needed
225 | #!**/packages/repositories.config
226 |
227 | # Windows Azure Build Output
228 | csx/
229 | *.build.csdef
230 |
231 | # Windows Store app package directory
232 | AppPackages/
233 |
234 | # Visual Studio cache files
235 | # files ending in .cache can be ignored
236 | *.[Cc]ache
237 | # but keep track of directories ending in .cache
238 | !*.[Cc]ache/
239 |
240 | # Others
241 | ClientBin/
242 | [Ss]tyle[Cc]op.*
243 | ~$*
244 | *~
245 | *.dbmdl
246 | *.dbproj.schemaview
247 | *.pfx
248 | *.publishsettings
249 | node_modules/
250 | orleans.codegen.cs
251 |
252 | # RIA/Silverlight projects
253 | Generated_Code/
254 |
255 | # Backup & report files from converting an old project file
256 | # to a newer Visual Studio version. Backup files are not needed,
257 | # because we have git ;-)
258 | _UpgradeReport_Files/
259 | Backup*/
260 | UpgradeLog*.XML
261 | UpgradeLog*.htm
262 |
263 | # SQL Server files
264 | *.mdf
265 | *.ldf
266 |
267 | # Business Intelligence projects
268 | *.rdl.data
269 | *.bim.layout
270 | *.bim_*.settings
271 |
272 | # Microsoft Fakes
273 | FakesAssemblies/
274 |
275 | # Node.js Tools for Visual Studio
276 | .ntvs_analysis.dat
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio LightSwitch build output
285 | **/*.HTMLClient/GeneratedArtifacts
286 | **/*.DesktopClient/GeneratedArtifacts
287 | **/*.DesktopClient/ModelManifest.xml
288 | **/*.Server/GeneratedArtifacts
289 | **/*.Server/ModelManifest.xml
290 | _Pvt_Extensions
291 |
292 | # PVS Studio for Linux output
293 | *.cl.cfg
294 |
295 | # Kate
296 | *.kate-swp
297 | *.swp
298 |
299 | # QtCreator
300 | build-*
301 |
302 | # Android
303 | *.apk
304 |
305 | # CLion
306 | cmake-build-*/
307 |
308 | # Others
309 | /.idea
310 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.12)
2 | project(HLMV VERSION 1.5.2)
3 |
4 | set(CMAKE_CXX_STANDARD 11)
5 |
6 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
7 | set(CMAKE_AUTOMOC ON)
8 | set(CMAKE_AUTOUIC ON)
9 | set(CMAKE_AUTORCC ON)
10 |
11 | # Find Qt5
12 | find_package(Qt5 REQUIRED Core Widgets OpenGL)
13 |
14 | # Find OpenGL : apt install libgl1-mesa-dev
15 | if (MSVC OR APPLE)
16 | find_package( opengl REQUIRED )
17 | else()
18 | set(OPENGL_LIBRARY GL)
19 | endif()
20 |
21 | if(APPLE)
22 | add_definitions(-DGL_SILENCE_DEPRECATION)
23 | endif()
24 |
25 | include_directories(src)
26 | include_directories(src/v)
27 | include_directories(src/m)
28 | include_directories(src/c)
29 |
30 |
31 | file(GLOB FILE_RES
32 | srv/v/hlmv.qrc
33 | )
34 | file(GLOB FILE_UI
35 | src/v/hlmv.ui
36 | )
37 |
38 | file(GLOB FILE_MOC
39 |
40 | src/v/hlmv.h
41 | src/v/myopenglwidget.h
42 | )
43 |
44 | file(GLOB FILE_SRC
45 | src/main.cpp
46 |
47 | src/v/hlmv.cpp
48 | src/v/myopenglwidget.cpp
49 |
50 | src/c/hlmv_application.cpp
51 | src/c/hlmv_tab.cpp
52 | src/c/hlmv_seq.cpp
53 | src/c/hlmv_event.cpp
54 | src/c/hlmv_texture.cpp
55 | src/c/hlmv_body.cpp
56 | src/c/hlmv_display.cpp
57 | src/c/hlmv_origin.cpp
58 | src/c/hlmv_cso_model.cpp
59 | src/c/hlmv_file.cpp
60 | src/c/qt_image.cpp
61 |
62 | src/m/mathlib.c
63 | src/m/studio_render.cpp
64 | src/m/studio_utils.cpp
65 | src/m/ViewerSettings.cpp
66 | src/m/gl_draw.cpp
67 | src/m/mod_decryptor.cpp
68 | src/m/IceKey.cpp
69 | src/m/TGAlib.cpp
70 | src/m/bmpread.cpp
71 | )
72 |
73 | if(APPLE)
74 | set(FILE_SRC_PLATFORM
75 | src/v/platform/macos/MacWindow.mm
76 | src/v/platform/macos/Touchbar.mm
77 | )
78 | endif()
79 |
80 | if(WIN32)
81 | set(FILE_SRC_PLATFORM src/v/platform/win32/AeroWindow.cpp)
82 | endif()
83 |
84 | # Moc settings
85 | qt5_wrap_cpp( GEN_MOC ${FILE_MOC})
86 | qt5_wrap_ui( GEN_UIC ${FILE_UI})
87 | qt5_add_resources( GEN_RCC ${FILE_RES})
88 |
89 | if(APPLE)
90 |
91 | set(APP_ICNS ./res/Icon.icns)
92 | set_source_files_properties(${APP_ICNS} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
93 |
94 | add_executable(HLMV MACOSX_BUNDLE ${FILE_SRC_PLATFORM} ${FILE_RES} ${FILE_UI} ${FILE_SRC} ${GEN_MOC} ${GEN_UIC} ${GEN_RCC} ${APP_ICNS})
95 |
96 | set_target_properties(HLMV PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in")
97 | set_target_properties(HLMV PROPERTIES MACOSX_BUNDLE_ICON_FILE "Icon.icns")
98 |
99 | qt5_use_modules(HLMV Core Widgets OpenGL MacExtras)
100 | target_link_libraries(HLMV "-framework AppKit")
101 | target_link_libraries(HLMV ${OPENGL_LIBRARY})
102 |
103 | set(CPACK_GENERATOR "DragNDrop")
104 | set(CPACK_PACKAGE_VENDOR "MoeMod")
105 | set(CPACK_PACKAGE_FILE_NAME "HLMV-Qt-macOS-${CMAKE_SYSTEM_PROCESSOR}")
106 | set(CPACK_DMG_VOLUME_NAME "HLMV")
107 | set(CPACK_DMG_FORMAT "UDBZ") #UDRW
108 | set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/cmake/DragNDrop/background.png")
109 | set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/cmake/DragNDrop/DS_Store")
110 |
111 | add_custom_target(HLMV_macdeployqt
112 | COMMAND "${Qt5_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/HLMV.app" "-always-overwrite"
113 | DEPENDS HLMV COMMENT "Copying Qt runtime binaries")
114 |
115 | include(CPack)
116 | include(CPackDMG)
117 | add_custom_target(HLMV_cpack_dmg
118 | COMMAND "cpack"
119 | DEPENDS HLMV HLMV_macdeployqt COMMENT "Generating DMG file")
120 |
121 | set(CMAKE_INSTALL_PREFIX "/Applications")
122 | install(TARGETS HLMV BUNDLE DESTINATION ".")
123 | elseif(WIN32)
124 | add_executable(HLMV WIN32 ${FILE_SRC_PLATFORM} ${FILE_RES} ${FILE_UI} ${FILE_SRC} ${GEN_MOC} ${GEN_UIC} ${GEN_RCC})
125 | qt5_use_modules(HLMV Core Widgets OpenGL WinExtras)
126 | target_link_libraries(HLMV ${OPENGL_LIBRARY})
127 |
128 | set(CPACK_GENERATOR "NSIS")
129 | set(CPACK_PACKAGE_NAME "Half-Life Model Viewer")
130 | set(CPACK_PACKAGE_VENDOR "MoeMod")
131 | set(CPACK_PACKAGE_FILE_NAME "HLMV-Qt-Win32-${CMAKE_SYSTEM_PROCESSOR}")
132 | set(CPACK_PACKAGE_INSTALL_DIRECTORY "HLMV")
133 | set(CPACK_PACKAGE_EXECUTABLES HLMV "${CPACK_PACKAGE_NAME}")
134 | set(CPACK_CREATE_DESKTOP_LINKS HLMV)
135 | set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
136 | set(CPACK_NSIS_MODIFY_PATH ON)
137 | set(CPACK_NSIS_MUI_FINISHPAGE_RUN HLMV)
138 |
139 | set(EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/bin")
140 | set(LIBRARY_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/bin")
141 | add_custom_target(HLMV_windeployqt
142 | COMMAND "${Qt5_DIR}/../../../bin/windeployqt" "${CMAKE_CURRENT_BINARY_DIR}/bin/HLMV.exe"
143 | DEPENDS HLMV COMMENT "Copying Qt runtime binaries")
144 |
145 | install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" DESTINATION ".")
146 |
147 | include(CPack)
148 | add_custom_target(HLMV_cpack_nsis
149 | COMMAND "echo" "Cleaning NSIS file"
150 | COMMAND "rd" "/s" "/q" "_CPack_Packages" "||" "echo" "Not Found, ignore..."
151 | COMMAND "cpack" "-V"
152 | DEPENDS HLMV HLMV_windeployqt COMMENT "Generating NSIS file")
153 |
154 | set(CMAKE_INSTALL_PREFIX "D:/tools/${PROJECT_NAME}")
155 |
156 | else()
157 | add_executable(HLMV ${FILE_SRC_PLATFORM} ${FILE_RES} ${FILE_UI} ${FILE_SRC} ${GEN_MOC} ${GEN_UIC} ${GEN_RCC})
158 | target_link_libraries(HLMV ${OPENGL_LIBRARY} Qt5::Core Qt5::Widgets Qt5::OpenGL)
159 | endif()
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HLMV-Qt
2 | Half-Life Model Viewer remixed with Qt5.
3 | Optimized for macOS.
4 |
5 | # Features
6 | - [x] Customized frameless UI on macOS
7 | - [x] Multi-Touch Bar support on macOS
8 | - [x] what the original HLMV does
9 | - [x] Import & Export with BMP/JPG/TIFF/PNG formats (Qt-featured)
10 | - [x] Screenshot to clipboard and/or image file
11 | - [x] CSO encrypted model loading
12 | - [x] CSO external textures loading
13 | - [x] Multi-modality interaction : touchbar, touchpad, touchscreen and mouse
14 | - [x] Automatically packaging and distribution with CMake/CPack : DMG for macOS and NSIS for Windows
15 |
16 | # Installation
17 | ## macOS
18 | Just download .dmg file and drag HLMV into Applications.
19 | Drag .mdl files into HLMV to open it.
20 | ## Windows
21 | Using the NSIS installer
22 | ## Linux
23 | WIP
24 |
25 | # Screenshots
26 |
27 |
28 | # Contribution
29 | Issue if you want more features.
30 | Pull requests are extremely welcomed.
31 |
--------------------------------------------------------------------------------
/cmake/DragNDrop/DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoeMod/HLMV-Qt/4dc620716d1329795ae30b24c92f28f737d33855/cmake/DragNDrop/DS_Store
--------------------------------------------------------------------------------
/cmake/DragNDrop/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoeMod/HLMV-Qt/4dc620716d1329795ae30b24c92f28f737d33855/cmake/DragNDrop/background.png
--------------------------------------------------------------------------------
/cmake/MacOSXBundleInfo.plist.in:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${MACOSX_BUNDLE_EXECUTABLE_NAME}
9 | CFBundleGetInfoString
10 | ${MACOSX_BUNDLE_INFO_STRING}
11 | CFBundleIconFile
12 | ${MACOSX_BUNDLE_ICON_FILE}
13 | CFBundleIdentifier
14 | ${MACOSX_BUNDLE_GUI_IDENTIFIER}
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleLongVersionString
18 | ${MACOSX_BUNDLE_LONG_VERSION_STRING}
19 | CFBundleName
20 | ${MACOSX_BUNDLE_BUNDLE_NAME}
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | ${MACOSX_BUNDLE_SHORT_VERSION_STRING}
25 | CFBundleSignature
26 | ????
27 | CFBundleVersion
28 | ${MACOSX_BUNDLE_BUNDLE_VERSION}
29 | CSResourcesFileMapped
30 |
31 | NSHumanReadableCopyright
32 | ${MACOSX_BUNDLE_COPYRIGHT}
33 | NSHighResolutionCapable
34 |
35 | NSPrincipalClass
36 | NSApplication
37 | CFBundleDocumentTypes
38 |
39 |
40 | CFBundleTypeExtensions
41 |
42 | mdl
43 |
44 | CFBundleTypeIconFile
45 | Icon.icns
46 | CFBundleTypeOSTypes
47 |
48 | ****
49 |
50 | CFBundleTypeName
51 | Half-Life model
52 | CFBundleTypeRole
53 | Viewer
54 | LSHandlerRank
55 | Default
56 | LSIsAppleDefaultForType
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/doc/screenshot_macos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoeMod/HLMV-Qt/4dc620716d1329795ae30b24c92f28f737d33855/doc/screenshot_macos.png
--------------------------------------------------------------------------------
/res/HLMV_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoeMod/HLMV-Qt/4dc620716d1329795ae30b24c92f28f737d33855/res/HLMV_128x128.png
--------------------------------------------------------------------------------
/res/HLMV_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoeMod/HLMV-Qt/4dc620716d1329795ae30b24c92f28f737d33855/res/HLMV_256x256.png
--------------------------------------------------------------------------------
/res/Icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoeMod/HLMV-Qt/4dc620716d1329795ae30b24c92f28f737d33855/res/Icon.icns
--------------------------------------------------------------------------------
/res/makeicns.sh:
--------------------------------------------------------------------------------
1 | sips -z 16 16 pic.png --out icons.iconset/icon_16x16.png
2 | sips -z 32 32 pic.png --out icons.iconset/icon_16x16@2x.png
3 | sips -z 32 32 pic.png --out icons.iconset/icon_32x32.png
4 | sips -z 64 64 pic.png --out icons.iconset/icon_32x32@2x.png
5 | sips -z 64 64 pic.png --out icons.iconset/icon_64x64.png
6 | sips -z 128 128 pic.png --out icons.iconset/icon_64x64@2x.png
7 | sips -z 128 128 pic.png --out icons.iconset/icon_128x128.png
8 | sips -z 256 256 pic.png --out icons.iconset/icon_128x128@2x.png
9 | sips -z 256 256 pic.png --out icons.iconset/icon_256x256.png
10 | sips -z 512 512 pic.png --out icons.iconset/icon_256x256@2x.png
11 | sips -z 512 512 pic.png --out icons.iconset/icon_512x512.png
12 | sips -z 1024 1024 pic.png --out icons.iconset/icon_512x512@2x.png
13 |
--------------------------------------------------------------------------------
/src/c/hlmv_application.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv_application.h"
2 | #include "ViewerSettings.h"
3 |
4 | #include
5 |
6 | HLMVApplication::HLMVApplication(int &argc, char **argv) : QApplication(argc, argv)
7 | {
8 | InitViewerSettings();
9 |
10 | setAttribute(Qt::AA_EnableHighDpiScaling, true);
11 | w.show();
12 | if(argc >= 2)
13 | {
14 | try
15 | {
16 | w.OpenFile(argv[1]);
17 | }
18 | catch(...)
19 | {
20 | // eat up the file-opening exception
21 | // and then invalid argument will be ignored
22 | }
23 | }
24 |
25 | }
26 |
27 | bool HLMVApplication::event(QEvent *event)
28 | {
29 | if (event->type() == QEvent::FileOpen)
30 | {
31 | QString filename = static_cast(event)->file();
32 | try
33 | {
34 | w.OpenFile(filename);
35 | }
36 | catch (...)
37 | {
38 |
39 | }
40 | return true;
41 | }
42 |
43 | return QApplication::event(event);
44 | }
45 |
--------------------------------------------------------------------------------
/src/c/hlmv_application.h:
--------------------------------------------------------------------------------
1 | #ifndef HLMV_APPLICATION_H
2 | #define HLMV_APPLICATION_H
3 |
4 | #include
5 | #include "v/hlmv.h"
6 |
7 | class HLMVApplication : public QApplication
8 | {
9 | public:
10 | HLMVApplication(int &argc, char **argv);
11 |
12 | public:
13 | bool event(QEvent *event) override;
14 |
15 | public:
16 | QtGuiApplication1 w;
17 | };
18 |
19 | #endif //HLMV_APPLICATION_H
20 |
--------------------------------------------------------------------------------
/src/c/hlmv_body.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 | #include "ViewerSettings.h"
3 | #include "StudioModel.h"
4 |
5 | void QtGuiApplication1::initBodyparts ()
6 | {
7 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
8 | if (hdr)
9 | {
10 | int i;
11 | mstudiobodyparts_t *pbodyparts = (mstudiobodyparts_t *) ((byte *) hdr + hdr->bodypartindex);
12 |
13 | ui.cBodypart->clear ();
14 | if (hdr->numbodyparts > 0)
15 | {
16 | for (i = 0; i < hdr->numbodyparts; i++)
17 | ui.cBodypart->addItem (pbodyparts[i].name);
18 |
19 | ui.cBodypart->setCurrentIndex (0);
20 | setBodypart(0);
21 |
22 | ui.cSubmodel->clear ();
23 | for (i = 0; i < pbodyparts[0].nummodels; i++)
24 | {
25 | char str[64];
26 | sprintf (str, "Submodel %d", i + 1);
27 | ui.cSubmodel->addItem (str);
28 | }
29 | ui.cSubmodel->setCurrentIndex (0);
30 | setSubmodel(0);
31 | }
32 | }
33 | }
34 |
35 |
36 |
37 | void QtGuiApplication1::setBodypart (int index)
38 | {
39 | if(index < 0)
40 | return;
41 |
42 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
43 | if (hdr)
44 | {
45 | ui.cBodypart->setCurrentIndex (index);
46 | if (index < hdr->numbodyparts)
47 | {
48 | mstudiobodyparts_t *pbodyparts = (mstudiobodyparts_t *) ((byte *) hdr + hdr->bodypartindex);
49 | ui.cSubmodel->clear ();
50 |
51 | for (int i = 0; i < pbodyparts[index].nummodels; i++)
52 | {
53 | char str[64];
54 | sprintf (str, "Submodel %d", i + 1);
55 | ui.cSubmodel->addItem (str);
56 | }
57 | ui.cSubmodel->setCurrentIndex (0);
58 | setSubmodel(0);
59 | }
60 | }
61 | }
62 |
63 | void QtGuiApplication1::setSubmodel (int index)
64 | {
65 | if(index < 0)
66 | return;
67 |
68 | g_studioModel.SetBodygroup (ui.cBodypart->currentIndex (), index);
69 | g_viewerSettings.submodels[ui.cBodypart->currentIndex ()] = index;
70 | }
71 |
72 | void QtGuiApplication1::initBoneControllers ()
73 | {
74 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
75 | if (hdr)
76 | {
77 | ui.cController->setEnabled (hdr->numbonecontrollers > 0);
78 | ui.slController->setEnabled (hdr->numbonecontrollers > 0);
79 | ui.cController->clear ();
80 |
81 | mstudiobonecontroller_t *pbonecontrollers = (mstudiobonecontroller_t *) ((byte *) hdr + hdr->bonecontrollerindex);
82 | for (int i = 0; i < hdr->numbonecontrollers; i++)
83 | {
84 | char str[32];
85 | if (pbonecontrollers[i].index == 4)
86 | sprintf (str, "Mouth");
87 | else
88 | sprintf (str, "Controller %d", pbonecontrollers[i].index);
89 | ui.cController->addItem (str);
90 | }
91 |
92 | if (hdr->numbonecontrollers > 0)
93 | {
94 | ui.cController->setCurrentIndex (0);
95 | ui.slController->setRange ((int) pbonecontrollers[0].start, (int) pbonecontrollers[0].end);
96 | ui.slController->setValue (0);
97 | }
98 |
99 | }
100 | }
101 |
102 | void QtGuiApplication1::setBoneController (int index)
103 | {
104 | if(index < 0)
105 | return;
106 |
107 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
108 | if (hdr)
109 | {
110 | mstudiobonecontroller_t *pbonecontrollers = (mstudiobonecontroller_t *) ((byte *) hdr + hdr->bonecontrollerindex);
111 | ui.slController->setRange ((int) pbonecontrollers[index].start, (int) pbonecontrollers[index].end);
112 | ui.slController->setValue (0);
113 | }
114 | }
115 |
116 |
117 |
118 | void QtGuiApplication1::setBoneControllerValue (int index, float value)
119 | {
120 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
121 | if (hdr)
122 | {
123 | mstudiobonecontroller_t *pbonecontrollers = (mstudiobonecontroller_t *) ((byte *) hdr + hdr->bonecontrollerindex);
124 | if (pbonecontrollers[index].index == 4)
125 | g_studioModel.SetMouth (value);
126 | else
127 | g_studioModel.SetController (pbonecontrollers[index].index, value);
128 |
129 | g_viewerSettings.controllers[index] = value;
130 | }
131 | }
132 |
133 | void QtGuiApplication1::setBoneControllerCurrentValue (int value)
134 | {
135 | if(!ui.cController->count())
136 | return;
137 | setBoneControllerValue(ui.cController->currentIndex(), static_cast(value));
138 | }
139 |
140 | void QtGuiApplication1::initSkins ()
141 | {
142 | studiohdr_t *hdr = g_studioModel.getTextureHeader ();
143 | if (hdr)
144 | {
145 | ui.cSkin->setEnabled (hdr->numskinfamilies > 0);
146 | ui.cSkin->clear ();
147 |
148 | for (int i = 0; i < hdr->numskinfamilies; i++)
149 | {
150 | char str[32];
151 | sprintf (str, "Skin %d", i + 1);
152 | ui.cSkin->addItem (str);
153 | }
154 |
155 | ui.cSkin->setCurrentIndex (0);
156 | setSkin(0);
157 | }
158 | }
159 |
160 | void QtGuiApplication1::setModelInfo ()
161 | {
162 | char str[1024];
163 |
164 | studiohdr_t *studioHdr = g_studioModel.getStudioHeader ();
165 | studiohdr_t *textureHdr = g_studioModel.getTextureHeader ();
166 |
167 | if (!studioHdr || !textureHdr)
168 | return;
169 |
170 | sprintf (str,
171 | "Bones: %d\n"
172 | "Bone Controllers: %d\n"
173 | "Hit Boxes: %d\n"
174 | "Sequences: %d\n"
175 | "Sequence Groups: %d\n",
176 | studioHdr->numbones,
177 | studioHdr->numbonecontrollers,
178 | studioHdr->numhitboxes,
179 | studioHdr->numseq,
180 | studioHdr->numseqgroups
181 | );
182 |
183 | ui.lModelInfo1->setText(str);
184 |
185 | sprintf (str,
186 | "Textures: %d\n"
187 | "Skin Families: %d\n"
188 | "Bodyparts: %d\n"
189 | "Attachments: %d\n"
190 | "Transitions: %d\n",
191 | textureHdr->numtextures,
192 | textureHdr->numskinfamilies,
193 | studioHdr->numbodyparts,
194 | studioHdr->numattachments,
195 | studioHdr->numtransitions);
196 |
197 | ui.lModelInfo2->setText (str);
198 | }
199 |
200 | void QtGuiApplication1::setSkin (int index)
201 | {
202 | if(index < 0)
203 | return;
204 |
205 | g_studioModel.SetSkin (index);
206 | g_viewerSettings.skin = index;
207 | }
208 |
209 |
210 |
--------------------------------------------------------------------------------
/src/c/hlmv_cso_model.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoeMod/HLMV-Qt/4dc620716d1329795ae30b24c92f28f737d33855/src/c/hlmv_cso_model.cpp
--------------------------------------------------------------------------------
/src/c/hlmv_display.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 | #include "ViewerSettings.h"
3 | #include "StudioModel.h"
4 |
5 | void QtGuiApplication1::updateViewSettings()
6 | {
7 | g_viewerSettings.renderMode = ui.cRenderMode->currentIndex();
8 |
9 | int value = ui.slTransparency->value ();
10 | g_viewerSettings.transparency = (float) value / 100.0f;
11 | ui.lTransparency->setText (QString().sprintf("Opacity: %d%%", value));
12 |
13 | g_viewerSettings.mirror = ui.cbMirror->isChecked();
14 | g_viewerSettings.showGround = ui.cbGround->isChecked();
15 | g_viewerSettings.showBackground = ui.cbBackground->isChecked();
16 | g_viewerSettings.showWireframeOverlay = ui.cbWireframeOverlay->isChecked();
17 |
18 | g_viewerSettings.useStencil = (!g_viewerSettings.use3dfx && g_viewerSettings.mirror);
19 |
20 | g_viewerSettings.showHitBoxes = ui.cbHitBoxes->isChecked();
21 | g_viewerSettings.showBones = ui.cbBones->isChecked();
22 | g_viewerSettings.showAttachments = ui.cbAttachments->isChecked();
23 | g_viewerSettings.showEyePosition = ui.cbEyePosition->isChecked();
24 |
25 | g_studioModel.scaleMeshes (static_cast(ui.leMeshScale->value()));
26 | g_studioModel.scaleBones (static_cast(ui.leBoneScale->value()));
27 |
28 | ui.openglwidget->update ();
29 | }
30 |
31 | void QtGuiApplication1::centerView ()
32 | {
33 | float min[3], max[3];
34 | g_studioModel.ExtractBbox (min, max);
35 |
36 | float dx = max[0] - min[0];
37 | float dy = max[1] - min[1];
38 | float dz = max[2] - min[2];
39 | float d = dx;
40 | if (dy > d)
41 | d = dy;
42 | if (dz > d)
43 | d = dz;
44 | g_viewerSettings.trans[0] = 0;
45 | g_viewerSettings.trans[1] = min[2] + dz / 2;
46 | g_viewerSettings.trans[2] = d * 1.0f;
47 | g_viewerSettings.rot[0] = -90.0f;
48 | g_viewerSettings.rot[1] = -90.0f;
49 | g_viewerSettings.rot[2] = 0.0f;
50 | ui.centralWidget->update ();
51 | }
52 |
--------------------------------------------------------------------------------
/src/c/hlmv_event.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 | #include "ViewerSettings.h"
3 | #include "StudioModel.h"
4 |
5 | void QtGuiApplication1::setEventInfo(int index)
6 | {
7 | char str[1024];
8 |
9 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
10 |
11 | if (!hdr)
12 | return;
13 |
14 | mstudioseqdesc_t *pseqdescs = (mstudioseqdesc_t *) ((byte *)hdr + hdr->seqindex);
15 |
16 | if(!ui.cEvent->count())
17 | {
18 | ui.lEventInfo->setText ("");
19 | return;
20 | }
21 |
22 | if (pseqdescs->numevents)
23 | {
24 | mstudioevent_t *pevents = (mstudioevent_t *) ((byte *)hdr + pseqdescs->eventindex) + index;
25 |
26 | sprintf (str,
27 | "Frame: %d "
28 | "Event: %d "
29 | "Type: %d\n"
30 | "Options: %s",
31 | pevents->frame,
32 | pevents->event,
33 | pevents->type,
34 | pevents->options
35 | );
36 | }
37 | else
38 | {
39 | str[0] = '\0';
40 | }
41 | ui.lEventInfo->setText (str);
42 | }
43 |
44 | void QtGuiApplication1::setEvent (int seq)
45 | {
46 | char str[64];
47 |
48 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
49 |
50 | if (!hdr)
51 | return;
52 |
53 | mstudioseqdesc_t *pseqdescs = (mstudioseqdesc_t *) ((byte *)hdr + hdr->seqindex); //g_viewerSettings.sequence;
54 |
55 | ui.cEvent->clear ();
56 | if (0 < pseqdescs[seq].numevents)
57 | {
58 | for (int i = 1; i <= pseqdescs[seq].numevents; i++)
59 | {
60 | sprintf (str, "Event %d", i);
61 | ui.cEvent->addItem (str);
62 | }
63 | }
64 | ui.cEvent->setCurrentIndex (0);
65 | setEventInfo(0);
66 | }
--------------------------------------------------------------------------------
/src/c/hlmv_file.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 | #include "StudioModel.h"
3 | #include "ViewerSettings.h"
4 | #include "mod_decryptor.h"
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include
15 |
16 | void QtGuiApplication1::dragEnterEvent(QDragEnterEvent *e)
17 | {
18 | // only 1 file is accepted
19 | if (e->mimeData()->urls().size() != 1)
20 | return;
21 |
22 | e->acceptProposedAction();
23 | }
24 |
25 | void QtGuiApplication1::dropEvent(QDropEvent *e)
26 | {
27 | QList urls = e->mimeData()->urls();
28 | if (urls.isEmpty())
29 | return;
30 |
31 | QString filename = urls.first().toLocalFile();
32 |
33 | if (filename.isEmpty())
34 | return;
35 |
36 | try
37 | {
38 | OpenFile(filename);
39 | }
40 | catch (const std::exception & e)
41 | {
42 | QMessageBox::warning(this, "Error", e.what());
43 | }
44 | }
45 |
46 | void QtGuiApplication1::OpenFile(QString qfilename) noexcept(false)
47 | {
48 | std::string sfilename = qfilename.toStdString();
49 | const char *filename = sfilename.c_str();
50 | g_studioModel.FreeModel ();
51 |
52 | studiohdr_t *phdr = g_studioModel.LoadModel (filename);
53 | if (!phdr)
54 | throw std::runtime_error("Error loading model.");
55 |
56 | bool bCSOTexture = false;
57 | if(Mod_IsCSOEncryptedModel(phdr))
58 | {
59 | if(QMessageBox::question(this,
60 | "CSO Model Helper",
61 | "This model is CSO-format and encrypted with IceKey. \nDo you want to decrypt it?"
62 | ) == QMessageBox::Yes)
63 | {
64 | Mod_DecryptModel(filename, phdr);
65 | bCSOTexture = true;
66 | }
67 | }
68 |
69 | if(!bCSOTexture && g_studioModel.hasCSOTexture(phdr))
70 | {
71 | if(QMessageBox::question(this,
72 | "CSO Model Helper",
73 | "This model is CSO-format and has external textures. \nDo you want to load them?"
74 | ) == QMessageBox::Yes)
75 | {
76 | bCSOTexture = true;
77 | }
78 | }
79 |
80 |
81 | if (!g_studioModel.PostLoadModel (phdr, filename))
82 | throw std::runtime_error("Error post-loading model.");
83 |
84 | if(bCSOTexture)
85 | g_studioModel.LoadModelTexturesCSO(phdr, (QFileInfo(qfilename).path() + "/texture").toStdString().c_str());
86 | else
87 | g_studioModel.LoadModelTextures(phdr);
88 |
89 |
90 | initSequences ();
91 | setEvent (0);
92 | initBodyparts ();
93 | initBoneControllers ();
94 | initSkins ();
95 | initTextures ();
96 | centerView ();
97 | strcpy (g_viewerSettings.modelFile, filename);
98 | setModelInfo ();
99 | g_viewerSettings.sequence = 0;
100 | setSequenceInfo ();
101 | g_viewerSettings.speedScale = 1.0f;
102 |
103 |
104 | setWindowFilePath(qfilename);
105 | setWindowTitle("HLMV - " + QFileInfo(qfilename).fileName());
106 |
107 | std::fill(std::begin(g_viewerSettings.submodels), std::end(g_viewerSettings.submodels), 0);
108 | std::fill(std::begin(g_viewerSettings.controllers), std::end(g_viewerSettings.controllers), 0);
109 | }
110 |
111 | void QtGuiApplication1::SaveFile(QString qfilename) noexcept(false)
112 | {
113 | std::string sfilename = qfilename.toStdString();
114 | const char *filename = sfilename.c_str();
115 |
116 | if (!filename || !filename[0])
117 | throw std::runtime_error("Empty file name.");
118 |
119 | if (!g_studioModel.getStudioHeader())
120 | throw std::runtime_error("No file opened.");
121 |
122 | bool result = g_studioModel.SaveModel(filename);
123 | if(!result)
124 | throw std::runtime_error("Error writing file.");
125 | }
126 |
127 | void QtGuiApplication1::CloseFile()
128 | {
129 | g_studioModel.FreeModel();
130 |
131 | ui.cSequence->clear();
132 | ui.cWpSequence->clear ();
133 | ui.lSequenceInfo1->clear();
134 | ui.lSequenceInfo2->clear();
135 | ui.slWeaponFrame->setRange(0, 0);
136 | ui.leWeaponFrame->setRange(0, 0);
137 | ui.slWpSpeedFPS->setRange(0, 0);
138 |
139 | ui.cEvent->clear ();
140 | ui.lEventInfo->clear ();
141 |
142 | ui.cSubmodel->clear ();
143 | ui.cBodypart->clear ();
144 | ui.cController->clear ();
145 | ui.cSkin->clear ();
146 |
147 | ui.cTextures->clear ();
148 | ui.cMesh->clear ();
149 |
150 | strcpy (g_viewerSettings.modelFile, "");
151 | ui.lModelInfo1->clear();
152 | ui.lModelInfo2->clear();
153 | }
154 |
--------------------------------------------------------------------------------
/src/c/hlmv_origin.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 | #include "ViewerSettings.h"
3 | #include "StudioModel.h"
4 |
5 |
6 | void QtGuiApplication1::updateWeaponOriginSettings()
7 | {
8 | g_viewerSettings.showCrosshair = ui.cbCrossHair->isChecked();
9 | g_viewerSettings.showGuideLines = ui.cbGuildlines->isChecked();
10 | g_viewerSettings.righthand = ui.cbRightHand->isChecked();
11 |
12 | g_viewerSettings.vieworigintrans[0] = static_cast(ui.leOriginX->value());
13 | g_viewerSettings.vieworigintrans[1] = static_cast(ui.leOriginY->value());
14 | g_viewerSettings.vieworigintrans[2] = static_cast(ui.leOriginZ->value());
15 |
16 | g_viewerSettings.viewfov = static_cast(ui.leFOV->value());
17 | g_viewerSettings.viewaspect = static_cast(ui.leAspec->value());
18 |
19 | }
--------------------------------------------------------------------------------
/src/c/hlmv_seq.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 | #include "ViewerSettings.h"
3 | #include "StudioModel.h"
4 |
5 | void QtGuiApplication1::initSequences ()
6 | {
7 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
8 | if (hdr)
9 | {
10 | ui.cSequence->clear();
11 | ui.cWpSequence->clear ();
12 | for (int i = 0; i < hdr->numseq; i++)
13 | {
14 | mstudioseqdesc_t *pseqdescs = (mstudioseqdesc_t *) ((byte *) hdr + hdr->seqindex);
15 | ui.cSequence->addItem (pseqdescs[i].label);
16 | ui.cWpSequence->addItem (pseqdescs[i].label);
17 | }
18 |
19 | ui.cSequence->setCurrentRow (0);
20 | ui.cWpSequence->setCurrentIndex (0);
21 | }
22 | }
23 |
24 | void QtGuiApplication1::setSequence(int index)
25 | {
26 | if(index < 0)
27 | return;
28 |
29 | ui.cSequence->setCurrentRow(index);
30 | ui.cWpSequence->setCurrentIndex (index);
31 |
32 | g_studioModel.SetSequence(index);
33 | g_viewerSettings.sequence = index;
34 | setSequenceInfo ();
35 | setEvent (index);
36 | }
37 |
38 | void QtGuiApplication1::setSequenceInfo ()
39 | {
40 | char str[1024];
41 |
42 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
43 |
44 | if (!hdr)
45 | return;
46 |
47 | mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *) ((byte *) hdr + hdr->seqindex) + g_viewerSettings.sequence;
48 |
49 | sprintf (str,
50 | "Sequence#: %d\n"
51 | "Frames: %d\n"
52 | "FPS: %d\n"
53 | "Blends: %d\n"
54 | "# of events: %d\n",
55 | g_viewerSettings.sequence,
56 | pseqdesc->numframes,
57 | (int)pseqdesc->fps,
58 | pseqdesc->numblends,
59 | pseqdesc->numevents
60 | );
61 |
62 | ui.lSequenceInfo1->setText (str);
63 | ui.lSequenceInfo2->setText (str);
64 |
65 | ui.slFrame->setRange(0, pseqdesc->numframes);
66 | ui.leFrame->setRange(0, pseqdesc->numframes);
67 | ui.slWeaponFrame->setRange(0, pseqdesc->numframes);
68 | ui.leWeaponFrame->setRange(0, pseqdesc->numframes);
69 |
70 | ui.slWpSpeedFPS->setValue(pseqdesc->fps);
71 | ui.slWpSpeedFPS->setRange(0, pseqdesc->fps * 10);
72 | }
73 |
74 | void QtGuiApplication1::OnSetAnimationPlaying(bool play)
75 | {
76 | ui.bPlaySequence->setChecked(play);
77 | ui.bPlaySequence2->setChecked(play);
78 |
79 | ui.openglwidget->setPaused(!play);
80 |
81 | int showcurrent = play ? 0 : g_studioModel.SetFrame (-1);
82 |
83 | ui.slFrame->setValue(showcurrent);
84 | ui.leFrame->setValue(showcurrent);
85 | ui.slWeaponFrame->setValue(showcurrent);
86 | ui.leWeaponFrame->setValue(showcurrent);
87 |
88 | ui.slFrame->setEnabled (!play);
89 | ui.leFrame->setEnabled (!play);
90 | ui.slWeaponFrame->setEnabled (!play);
91 | ui.leWeaponFrame->setEnabled (!play);
92 | }
93 |
94 | void QtGuiApplication1::OnSetAnimationCurrentFrame(int v)
95 | {
96 | ui.slFrame->setValue (v);
97 | ui.leFrame->setValue (v);
98 | ui.slWeaponFrame->setValue (v);
99 | ui.leWeaponFrame->setValue (v);
100 |
101 | g_studioModel.SetFrame (v);
102 | }
103 |
104 | void QtGuiApplication1::OnSetAnimationFPS(double fps)
105 | {
106 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
107 | if (!hdr)
108 | return;
109 | mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *) ((byte *) hdr + hdr->seqindex) + g_viewerSettings.sequence;
110 | //g_viewerSettings.speedScale = static_cast(fps) / pseqdesc->fps;
111 | pseqdesc->fps = fps;
112 |
113 | }
114 |
115 |
--------------------------------------------------------------------------------
/src/c/hlmv_tab.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 | #include "ViewerSettings.h"
3 |
4 | void QtGuiApplication1::OnTabChanged(int n)
5 | {
6 | g_viewerSettings.showTexture = (n == 2);
7 |
8 | if (n == 4)
9 | {
10 | g_viewerSettings.vieworiginmode = true;
11 | g_viewerSettings.yaw = 74.0f;
12 | }
13 | else
14 | {
15 | g_viewerSettings.yaw = 65.0f;
16 | g_viewerSettings.vieworiginmode = false;
17 | }
18 | #ifdef Q_OS_MAC
19 | // update touchbar when tab changed
20 | InstallTouchBar();
21 | #endif
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/src/c/hlmv_texture.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 | #include "ViewerSettings.h"
3 | #include "StudioModel.h"
4 | #include "qt_image.h"
5 |
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | void QtGuiApplication1::initTextures ()
13 | {
14 | studiohdr_t *hdr = g_studioModel.getTextureHeader ();
15 | if (hdr)
16 | {
17 | ui.cTextures->clear ();
18 | mstudiotexture_t *ptextures = (mstudiotexture_t *) ((byte *) hdr + hdr->textureindex);
19 | for (int i = 0; i < hdr->numtextures; i++)
20 | ui.cTextures->addItem (ptextures[i].name);
21 | ui.cTextures->setCurrentIndex (0);
22 | g_viewerSettings.texture = 0;
23 | if (hdr->numtextures > 0)
24 | {
25 | ui.cbChrome->setChecked ((ptextures->flags & STUDIO_NF_CHROME) == STUDIO_NF_CHROME);
26 | ui.cbAdditive->setChecked ((ptextures->flags & STUDIO_NF_ADDITIVE) == STUDIO_NF_ADDITIVE);
27 | ui.cbTransparent->setChecked ((ptextures->flags & STUDIO_NF_TRANSPARENT) == STUDIO_NF_TRANSPARENT);
28 | ui.cbFullBright->setChecked ((ptextures->flags & STUDIO_NF_FULLBRIGHT) == STUDIO_NF_FULLBRIGHT);
29 | initMeshList(0);
30 | }
31 | }
32 | }
33 |
34 | void QtGuiApplication1::setMesh (int index)
35 | {
36 | if (index >= 0)
37 | {
38 | g_viewerSettings.showAllMeshes = true;
39 | if (g_viewerSettings.meshCount >= 1 && index < g_viewerSettings.meshCount)
40 | g_viewerSettings.showAllMeshes = false;
41 | g_viewerSettings.mesh = index;
42 | }
43 | }
44 |
45 | void QtGuiApplication1::initMeshList (int index)
46 | {
47 | char str[64];
48 | int i, j, k = 0, l;
49 |
50 | studiohdr_t *hdr = g_studioModel.getStudioHeader ();
51 |
52 | if (!hdr)
53 | return;
54 |
55 | ui.cMesh->clear ();
56 | for (i = 0; i < hdr->numbodyparts; i++)
57 | {
58 | mstudiobodyparts_t *pbodyparts = (mstudiobodyparts_t *) ((byte *) hdr + hdr->bodypartindex) + i;
59 | for (j = 0; j < pbodyparts->nummodels; j++)
60 | {
61 | mstudiomodel_t *pmodels = (mstudiomodel_t *) ((byte *) hdr + pbodyparts->modelindex) + j;
62 | for (l = 0; l < pmodels->nummesh; l++)
63 | {
64 | mstudiomesh_t *pmesh = (mstudiomesh_t *) ((byte *) hdr + pmodels->meshindex) + l;
65 | if (pmesh->skinref == index)
66 | {
67 | sprintf (str, "Mesh %d", ++k);
68 | ui.cMesh->addItem (str);
69 | }
70 | }
71 | }
72 | }
73 |
74 | ui.cMesh->addItem ("All");
75 |
76 | g_viewerSettings.mesh = 0;
77 | g_viewerSettings.meshCount = k;
78 | ui.cMesh->setCurrentIndex (k);
79 | setMesh(k);
80 | }
81 |
82 | void QtGuiApplication1::setTextureCurrent(int index)
83 | {
84 | if (index < 0)
85 | return;
86 |
87 | g_viewerSettings.texture = index;
88 | studiohdr_t *hdr = g_studioModel.getTextureHeader ();
89 | if (hdr)
90 | {
91 | mstudiotexture_t *ptexture = (mstudiotexture_t *) ((byte *) hdr + hdr->textureindex) + index;
92 | ui.lTexSize->setText (QString().sprintf("Texture (size: %d x %d)", ptexture->width, ptexture->height));
93 | ui.cbChrome->setChecked ((ptexture->flags & STUDIO_NF_CHROME) == STUDIO_NF_CHROME);
94 | ui.cbAdditive->setChecked ((ptexture->flags & STUDIO_NF_ADDITIVE) == STUDIO_NF_ADDITIVE);
95 | ui.cbTransparent->setChecked ((ptexture->flags & STUDIO_NF_TRANSPARENT) == STUDIO_NF_TRANSPARENT);
96 | ui.cbFullBright->setChecked ((ptexture->flags & STUDIO_NF_FULLBRIGHT) == STUDIO_NF_FULLBRIGHT);
97 | }
98 | initMeshList (index);
99 | ui.openglwidget->update ();
100 | }
101 |
102 | void QtGuiApplication1::updateTextureFlags()
103 | {
104 | studiohdr_t *hdr = g_studioModel.getTextureHeader ();
105 | if (hdr)
106 | {
107 | mstudiotexture_t *ptexture = (mstudiotexture_t *) ((byte *) hdr + hdr->textureindex) + g_viewerSettings.texture;
108 | if (ui.cbChrome->isChecked ())
109 | {
110 | ptexture->flags &= ~STUDIO_NF_TRANSPARENT;
111 | ptexture->flags |= STUDIO_NF_CHROME;
112 | ui.cbTransparent->setChecked (false);
113 | }
114 | else
115 | ptexture->flags &= ~STUDIO_NF_CHROME;
116 |
117 | if (ui.cbAdditive->isChecked ())
118 | {
119 | ptexture->flags &= ~STUDIO_NF_TRANSPARENT;
120 | ptexture->flags |= STUDIO_NF_ADDITIVE;
121 | ui.cbTransparent->setChecked (false);
122 | }
123 | else
124 | ptexture->flags &= ~STUDIO_NF_ADDITIVE;
125 |
126 | if (ui.cbTransparent->isChecked ())
127 | {
128 | ptexture->flags &= ~(STUDIO_NF_ADDITIVE|STUDIO_NF_CHROME);
129 | ptexture->flags |= STUDIO_NF_TRANSPARENT;
130 | ui.cbChrome->setChecked (false);
131 | ui.cbAdditive->setChecked (false);
132 | }
133 | else
134 | ptexture->flags &= ~STUDIO_NF_TRANSPARENT;
135 |
136 | if (ui.cbFullBright->isChecked ())
137 | {
138 | ptexture->flags |= STUDIO_NF_FULLBRIGHT;
139 | }
140 | else
141 | ptexture->flags &= ~STUDIO_NF_FULLBRIGHT;
142 | }
143 | }
144 |
145 | void QtGuiApplication1::updateTextureUVDisplay()
146 | {
147 | g_viewerSettings.showUVMap = ui.cbUVMap->isChecked ();
148 | g_viewerSettings.showUVMapOverlay = ui.cbUVMapOverlay->isChecked ();
149 | g_viewerSettings.showSmoothLines = ui.cbAA->isChecked ();
150 | }
151 |
152 | void QtGuiApplication1::setTextureScale(int texScale)
153 | {
154 | g_viewerSettings.textureScale = 1.0f + static_cast(texScale) * 4.0f / 100.0f;
155 | ui.lTextureScale->setText (QString().sprintf("Scale Texture View (%.fx)", g_viewerSettings.textureScale));
156 | //d_GlWindow->redraw ();
157 | }
158 |
159 | void QtGuiApplication1::onImportTexture()
160 | {
161 | try
162 | {
163 | const QString filename = getOpenFileImagePath(this);
164 | if(filename.isEmpty())
165 | return;
166 |
167 | const QImage qiOriginal = getOpenImage(filename); // may throw
168 |
169 | studiohdr_t *phdr = g_studioModel.getTextureHeader ();
170 | if (!phdr)
171 | throw std::runtime_error("studiohdr_t *phdr not available");
172 |
173 | mstudiotexture_t *ptexture = (mstudiotexture_t *) ((byte *) phdr + phdr->textureindex) + g_viewerSettings.texture;
174 |
175 | const QImage qi = qiOriginal.scaled(ptexture->width, ptexture->height).convertToFormat(QImage::Format::Format_Indexed8);
176 | if(qi.isNull())
177 | throw std::runtime_error("Can not transform texture");
178 |
179 | std::copy_n (qi.bits(), ptexture->width * ptexture->height, ((byte *) phdr + ptexture->index));
180 |
181 | // RGB_RGB_RGB_ => RGBRGB
182 | QVector qv = qi.colorTable();
183 | for(std::size_t i = 0; i< 256; ++i)
184 | {
185 | byte *p = (byte *) phdr + ptexture->index + ptexture->width * ptexture->height + 3 * i;
186 | QColor color = qv[i];
187 | p[0] = static_cast(color.red());
188 | p[1] = static_cast(color.green());
189 | p[2] = static_cast(color.blue());
190 | }
191 |
192 | g_studioModel.UploadTexture (ptexture, (byte *) phdr + ptexture->index, (byte *) phdr + ptexture->index + ptexture->width * ptexture->height, g_viewerSettings.texture + 3);
193 |
194 | ui.openglwidget->update ();
195 | }
196 | catch(const std::exception &e)
197 | {
198 | return QMessageBox::critical(this, "Error", e.what()), void();
199 | }
200 | }
201 |
202 | void QtGuiApplication1::onExportTexture()
203 | {
204 | try
205 | {
206 | studiohdr_t *phdr = g_studioModel.getTextureHeader ();
207 | phdr ? void() : throw std::runtime_error("studiohdr_t *phdr not available");
208 |
209 | mstudiotexture_t *ptexture = (mstudiotexture_t *) ((byte *) phdr + phdr->textureindex) + g_viewerSettings.texture;
210 | ptexture ? void() : throw std::runtime_error("mstudiotexture_t *ptexture not available");
211 |
212 | QString filename = getSaveFileImagePath(this, {}, ptexture->name);
213 | if(filename.isEmpty())
214 | return;
215 |
216 | QImage qi( ((byte *) phdr + ptexture->index),
217 | ptexture->width,
218 | ptexture->height,
219 | QImage::Format::Format_Indexed8
220 | );
221 | qi.isNull() ? void() : throw std::runtime_error("cannot convert to QImage");
222 |
223 | // RGBRGB => RGB_RGB_RGB_
224 | {
225 | QVector qv(256);
226 | for(std::size_t i = 0; i< 256; ++i)
227 | {
228 | const byte *p = (byte *) phdr + ptexture->index + ptexture->width * ptexture->height + 3 * i;
229 | QRgb rgb = QColor(p[0], p[1], p[2]).rgb();
230 | qv[i] = rgb;
231 | }
232 | qi.setColorTable(std::move(qv));
233 | }
234 |
235 | saveImageTo(qi, filename);
236 | }
237 | catch (const std::exception & e)
238 | {
239 | return QMessageBox::critical(this, "Error", e.what()), void();
240 | }
241 | }
--------------------------------------------------------------------------------
/src/c/qt_image.cpp:
--------------------------------------------------------------------------------
1 | #include "qt_image.h"
2 |
3 | #include "TGAlib.h"
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | QString getOpenFileImagePath(QWidget *parent, const QString &caption, const QString &dir)
10 | {
11 | return QFileDialog::getOpenFileName(parent, caption, dir, "Image file(*.bmp;*.jpg;*.png;*.tiff;*.gif;*.tga);;BMP file (*.bmp);;JPG file (*.jpg);;PNG file (*.png);;TIFF file (*.tiff);;GIF file (*.gif);;TGA file (*.tga)");
12 | }
13 |
14 | QString getSaveFileImagePath(QWidget *parent, const QString &caption, const QString &dir)
15 | {
16 | return QFileDialog::getSaveFileName(parent, caption, dir, "BMP file (*.bmp);;JPG file (*.jpg);;PNG file (*.png);;TIFF file (*.tiff);;GIF file(*.gif)");
17 | }
18 |
19 | QImage getOpenImage(const QString &filename) noexcept(false)
20 | {
21 | if(filename.isEmpty())
22 | throw std::runtime_error("empty path");
23 |
24 | const QFileInfo file(filename);
25 | if(!file.exists())
26 | throw std::runtime_error("Image not exists : " + filename.toStdString());
27 |
28 | if(!file.suffix().compare("tga", Qt::CaseInsensitive))
29 | {
30 | // note that const char * will expire on next line
31 | std::unique_ptr tga(
32 | tgaLoad(file.absoluteFilePath().toStdString().c_str()),
33 | +[](void *p){tgaDestroy(reinterpret_cast(p));}
34 | );
35 |
36 | if(!tga || tga->status != TGA_OK)
37 | throw std::runtime_error("Error reading tga file");
38 |
39 | QImage::Format format = QImage::Format_Invalid;
40 | if(tga->pixelDepth == 32)
41 | format = QImage::Format_RGBA8888;
42 | else if(tga->pixelDepth == 24)
43 | format = QImage::Format_RGB888;
44 |
45 | if(format == QImage::Format_Invalid)
46 | throw std::runtime_error("TGA format not supported");
47 |
48 | return QImage(tga->imageData, tga->width, tga->height, format, tga.get_deleter(), tga.release());
49 | }
50 | else
51 | {
52 | const QImage qiOriginal(file.absoluteFilePath());
53 | if(qiOriginal.isNull())
54 | throw std::runtime_error("Image not available");
55 |
56 | return qiOriginal;
57 | }
58 | }
59 |
60 | void saveImageTo(const QImage& what, const QString &filename) noexcept(false)
61 | {
62 | return what.save(filename) ? void() : throw("Error writing texture to : " + filename.toStdString());
63 | }
64 |
--------------------------------------------------------------------------------
/src/c/qt_image.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef HLMV_QT_IMAGE_H
3 | #define HLMV_QT_IMAGE_H
4 |
5 | #include
6 |
7 | QString getOpenFileImagePath(QWidget *parent = Q_NULLPTR,
8 | const QString &caption = QString(),
9 | const QString &dir = QString());
10 |
11 | QString getSaveFileImagePath(QWidget *parent = Q_NULLPTR,
12 | const QString &caption = QString(),
13 | const QString &dir = QString());
14 |
15 | QImage getOpenImage(const QString &filename) noexcept(false);
16 | void saveImageTo(const QImage& what, const QString &filename) noexcept(false);
17 |
18 | #endif //HLMV_QT_IMAGE_H
19 |
--------------------------------------------------------------------------------
/src/gl.h:
--------------------------------------------------------------------------------
1 | #include
--------------------------------------------------------------------------------
/src/m/IceKey.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "IceKey.hpp"
3 |
4 | struct TIceSubKey
5 | {
6 | uint32_t dwValue[3];
7 | };
8 |
9 | static bool g_bIceSBoxesInitialised = false;
10 | static uint32_t g_pdwIceSBox[4][1024];
11 |
12 | static const int g_piIceSMod[4][4] =
13 | {
14 | { 333, 313, 505, 369 },
15 | { 379, 375, 319, 391 },
16 | { 361, 445, 451, 397 },
17 | { 397, 425, 395, 505 }
18 | };
19 |
20 | static const int g_piIceSXor[4][4] =
21 | {
22 | { 0x83, 0x85, 0x9b, 0xcd },
23 | { 0xcc, 0xa7, 0xad, 0x41 },
24 | { 0x4b, 0x2e, 0xd4, 0x33 },
25 | { 0xea, 0xcb, 0x2e, 0x04 }
26 | };
27 |
28 | static const uint32_t g_pdwIcePBox[32] =
29 | {
30 | 0x00000001, 0x00000080, 0x00000400, 0x00002000,
31 | 0x00080000, 0x00200000, 0x01000000, 0x40000000,
32 | 0x00000008, 0x00000020, 0x00000100, 0x00004000,
33 | 0x00010000, 0x00800000, 0x04000000, 0x20000000,
34 | 0x00000004, 0x00000010, 0x00000200, 0x00008000,
35 | 0x00020000, 0x00400000, 0x08000000, 0x10000000,
36 | 0x00000002, 0x00000040, 0x00000800, 0x00001000,
37 | 0x00040000, 0x00100000, 0x02000000, 0x80000000
38 | };
39 |
40 | static const int g_piIceKeyRotation[16] =
41 | {
42 | 0, 1, 2, 3, 2, 1, 3, 0,
43 | 1, 3, 2, 0, 3, 1, 0, 2
44 | };
45 |
46 | static uint32_t GFMultiply(uint32_t a, uint32_t b, uint32_t m)
47 | {
48 | uint32_t res = 0;
49 |
50 | while (b)
51 | {
52 | if (b & 1)
53 | res ^= a;
54 |
55 | a <<= 1;
56 | b >>= 1;
57 |
58 | if (a >= 256)
59 | a ^= m;
60 | }
61 |
62 | return res;
63 | }
64 |
65 | static uint32_t GFExp7(uint32_t b, uint32_t m)
66 | {
67 | uint32_t x;
68 |
69 | if (b == 0)
70 | return 0;
71 |
72 | x = GFMultiply(b, b, m);
73 | x = GFMultiply(b, x, m);
74 | x = GFMultiply(x, x, m);
75 | return GFMultiply(b, x, m);
76 | }
77 |
78 | static uint32_t IcePerm32(uint32_t x)
79 | {
80 | uint32_t res = 0;
81 | const uint32_t *pbox = g_pdwIcePBox;
82 |
83 | while (x)
84 | {
85 | if (x & 1)
86 | res |= *pbox;
87 |
88 | pbox++;
89 | x >>= 1;
90 | }
91 |
92 | return res;
93 | }
94 |
95 | static void IceSBoxesInitialize(void)
96 | {
97 | int i;
98 |
99 | for (i = 0; i < 1024; ++i)
100 | {
101 | int col = (i >> 1) & 0xff;
102 | int row = (i & 0x1) | ((i & 0x200) >> 8);
103 | uint32_t x;
104 |
105 | x = GFExp7(col ^ g_piIceSXor[0][row], g_piIceSMod[0][row]) << 24;
106 | g_pdwIceSBox[0][i] = IcePerm32(x);
107 |
108 | x = GFExp7(col ^ g_piIceSXor[1][row], g_piIceSMod[1][row]) << 16;
109 | g_pdwIceSBox[1][i] = IcePerm32(x);
110 |
111 | x = GFExp7(col ^ g_piIceSXor[2][row], g_piIceSMod[2][row]) << 8;
112 | g_pdwIceSBox[2][i] = IcePerm32(x);
113 |
114 | x = GFExp7(col ^ g_piIceSXor[3][row], g_piIceSMod[3][row]);
115 | g_pdwIceSBox[3][i] = IcePerm32(x);
116 | }
117 | }
118 |
119 | static uint32_t IceFunction(uint32_t dwValue, const TIceSubKey *sk)
120 | {
121 | uint32_t tl, tr;
122 | uint32_t al, ar;
123 |
124 | tl = ((dwValue >> 16) & 0x3ff) | (((dwValue >> 14) | (dwValue << 18)) & 0xffc00);
125 | tr = (dwValue & 0x3ff) | ((dwValue << 2) & 0xffc00);
126 |
127 | al = sk->dwValue[2] & (tl ^ tr);
128 | ar = al ^ tr;
129 | al ^= tl;
130 |
131 | al ^= sk->dwValue[0];
132 | ar ^= sk->dwValue[1];
133 |
134 | return g_pdwIceSBox[0][al >> 10] | g_pdwIceSBox[1][al & 0x3ff] | g_pdwIceSBox[2][ar >> 10] | g_pdwIceSBox[3][ar & 0x3ff];
135 | }
136 |
137 | CIceKey::CIceKey(int n) : m_iSize(0), m_iRounds(0)
138 | {
139 | if (!g_bIceSBoxesInitialised)
140 | {
141 | IceSBoxesInitialize();
142 | g_bIceSBoxesInitialised = 1;
143 | }
144 |
145 | if (n < 1)
146 | {
147 | m_iSize = 1;
148 | m_iRounds = 8;
149 | }
150 | else
151 | {
152 | m_iSize = n;
153 | m_iRounds = n * 16;
154 | }
155 |
156 | m_pKeySchedule = new TIceSubKey[m_iRounds];
157 | }
158 |
159 | CIceKey::~CIceKey(void)
160 | {
161 | int i, j;
162 |
163 | for (i = 0; i < m_iRounds; ++i)
164 | {
165 | for (j = 0; j < 3; ++j)
166 | m_pKeySchedule[i].dwValue[j] = 0;
167 | }
168 |
169 | m_iRounds = m_iSize = 0;
170 |
171 | delete[] m_pKeySchedule;
172 | }
173 |
174 | void CIceKey::BuildSchedule(unsigned short *usKeyBuilder, int n, const int *cpiKeyRotation)
175 | {
176 | int i, j, k;
177 |
178 | for (i = 0; i < 8; ++i)
179 | {
180 | int iKeyRotation = cpiKeyRotation[i];
181 | TIceSubKey *pSubKey = &m_pKeySchedule[n + i];
182 |
183 | for (j = 0; j < 3; ++j)
184 | pSubKey->dwValue[j] = 0;
185 |
186 | for (j = 0; j < 15; ++j)
187 | {
188 | uint32_t *pdwCurrentSubKey = &pSubKey->dwValue[j % 3];
189 |
190 | for (k = 0; k < 4; ++k)
191 | {
192 | unsigned short *pusCurrentKeyBuilder = &usKeyBuilder[(iKeyRotation + k) & 3];
193 | unsigned short bit = *pusCurrentKeyBuilder & 1;
194 |
195 | *pdwCurrentSubKey = (*pdwCurrentSubKey << 1) | bit;
196 | *pusCurrentKeyBuilder = (*pusCurrentKeyBuilder >> 1) | ((bit ^ 1) << 15);
197 | }
198 | }
199 | }
200 | }
201 |
202 | void CIceKey::SetKey(const uint8_t *pKey)
203 | {
204 | int i, j;
205 | unsigned short pusKeyBuilder[4];
206 |
207 | if (m_iRounds == 8)
208 | {
209 | for (i = 0; i < 4; ++i)
210 | pusKeyBuilder[3 - i] = (pKey[i << 1] << 8) | pKey[(i << 1) + 1];
211 |
212 | this->BuildSchedule(pusKeyBuilder, 0, g_piIceKeyRotation);
213 | return;
214 | }
215 |
216 | for (i = 0; i < m_iSize; ++i)
217 | {
218 | for (j = 0; j < 4; ++j)
219 | pusKeyBuilder[3 - j] = (pKey[(i << 3) + (j << 1)] << 8) | pKey[(i << 3) + (j << 1) + 1];
220 |
221 | this->BuildSchedule(pusKeyBuilder, i << 3, g_piIceKeyRotation);
222 | this->BuildSchedule(pusKeyBuilder, m_iRounds - 8 - (i << 3), &g_piIceKeyRotation[8]);
223 | }
224 | }
225 |
226 | void CIceKey::Encrypt(const uint8_t pPlainText[8], uint8_t pCipherText[8]) const
227 | {
228 | int i;
229 | uint32_t l, r;
230 |
231 | l = (((uint32_t)pPlainText[0]) << 24) | (((uint32_t)pPlainText[1]) << 16) | (((uint32_t)pPlainText[2]) << 8) | pPlainText[3];
232 | r = (((uint32_t)pPlainText[4]) << 24) | (((uint32_t)pPlainText[5]) << 16) | (((uint32_t)pPlainText[6]) << 8) | pPlainText[7];
233 |
234 | for (i = 0; i < m_iRounds; i += 2)
235 | {
236 | l ^= IceFunction(r, &m_pKeySchedule[i]);
237 | r ^= IceFunction(l, &m_pKeySchedule[i + 1]);
238 | }
239 |
240 | for (i = 0; i < 4; ++i)
241 | {
242 | pCipherText[3 - i] = (uint8_t)(r & 0xff);
243 | pCipherText[7 - i] = (uint8_t)(l & 0xff);
244 |
245 | r >>= 8;
246 | l >>= 8;
247 | }
248 | }
249 |
250 | void CIceKey::Decrypt(const uint8_t pCipherText[8], uint8_t pPlainText[8]) const
251 | {
252 | int i;
253 | uint32_t l, r;
254 |
255 | l = (((uint32_t)pCipherText[0]) << 24) | (((uint32_t)pCipherText[1]) << 16) | (((uint32_t)pCipherText[2]) << 8) | pCipherText[3];
256 | r = (((uint32_t)pCipherText[4]) << 24) | (((uint32_t)pCipherText[5]) << 16) | (((uint32_t)pCipherText[6]) << 8) | pCipherText[7];
257 |
258 | for (i = m_iRounds - 1; i > 0; i -= 2)
259 | {
260 | l ^= IceFunction(r, &m_pKeySchedule[i]);
261 | r ^= IceFunction(l, &m_pKeySchedule[i - 1]);
262 | }
263 |
264 | for (i = 0; i < 4; ++i)
265 | {
266 | pPlainText[3 - i] = (uint8_t)(r & 0xff);
267 | pPlainText[7 - i] = (uint8_t)(l & 0xff);
268 |
269 | r >>= 8;
270 | l >>= 8;
271 | }
272 | }
273 |
--------------------------------------------------------------------------------
/src/m/IceKey.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "stdint.h"
4 |
5 | struct TIceSubKey;
6 |
7 | class CIceKey
8 | {
9 | public:
10 | CIceKey(int n = 0);
11 | ~CIceKey(void);
12 |
13 | void SetKey(const uint8_t *pKey);
14 |
15 | void Encrypt(const uint8_t pPlainText[8], uint8_t pCipherText[8]) const;
16 | void Decrypt(const uint8_t pCipherText[8], uint8_t pPlainText[8]) const;
17 |
18 | int GetKeySize() const { return m_iSize << 3; }
19 | static int GetBlockSize() { return 8; }
20 |
21 | private:
22 | void BuildSchedule(unsigned short *usKeyBuilder, int n, const int *cpiKeyRotation);
23 |
24 | int m_iSize;
25 | int m_iRounds;
26 | TIceSubKey *m_pKeySchedule;
27 | };
28 |
--------------------------------------------------------------------------------
/src/m/StudioModel.h:
--------------------------------------------------------------------------------
1 | /***
2 | *
3 | * Copyright (c) 1998, Valve LLC. All rights reserved.
4 | *
5 | * This product contains software technology licensed from Id
6 | * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
7 | * All Rights Reserved.
8 | *
9 | ****/
10 |
11 | #ifndef INCLUDED_STUDIOMODEL
12 | #define INCLUDED_STUDIOMODEL
13 |
14 |
15 |
16 | #ifndef byte
17 | typedef unsigned char byte;
18 | #endif // byte
19 |
20 |
21 |
22 | #include "mathlib.h"
23 | #include "studio.h"
24 |
25 |
26 |
27 | class StudioModel
28 | {
29 | public:
30 | studiohdr_t *getStudioHeader () const { return m_pstudiohdr; }
31 | studiohdr_t *getTextureHeader () const { return m_ptexturehdr; }
32 | studiohdr_t *getAnimHeader (int i) const { return m_panimhdr[i]; }
33 |
34 | void UploadTexture( const mstudiotexture_t *ptexture, const byte *data, const byte *pal, int name );
35 | bool UploadTextureTGA( mstudiotexture_t *ptexture, const char *path, int name );
36 | bool UploadTextureBMP( mstudiotexture_t *ptexture, const char *path, int name );
37 | void FreeModel ();
38 |
39 | studiohdr_t *LoadModel( const char *modelname );
40 | void LoadModelTextures( const studiohdr_t *phdr );
41 | void LoadModelTexturesCSO( studiohdr_t *phdr, const char *texturePath );
42 | bool PostLoadModel ( studiohdr_t *phdr, const char *modelname );
43 |
44 | bool SaveModel ( const char *modelname );
45 | void DrawModel( void );
46 | void AdvanceFrame( float dt );
47 | int SetFrame (int nFrame);
48 |
49 | void ExtractBbox( float *mins, float *maxs );
50 |
51 | int SetSequence( int iSequence );
52 | int GetSequence( void );
53 | void GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed );
54 |
55 | float SetController( int iController, float flValue );
56 | float SetMouth( float flValue );
57 | float SetBlending( int iBlender, float flValue );
58 | int SetBodygroup( int iGroup, int iValue );
59 | int SetSkin( int iValue );
60 |
61 | void scaleMeshes (float scale);
62 | void scaleBones (float scale);
63 |
64 | static bool hasCSOTexture(const studiohdr_t *phdr);
65 |
66 | private:
67 | // entity settings
68 | vec3_t m_origin;
69 | vec3_t m_angles;
70 | int m_sequence; // sequence index
71 | float m_frame; // frame
72 | int m_bodynum; // bodypart selection
73 | int m_skinnum; // skin group selection
74 | byte m_controller[4]; // bone controllers
75 | byte m_blending[2]; // animation blending
76 | byte m_mouth; // mouth position
77 | bool m_owntexmodel; // do we have a modelT.mdl ?
78 |
79 | // internal data
80 | studiohdr_t *m_pstudiohdr;
81 | mstudiomodel_t *m_pmodel;
82 |
83 | studiohdr_t *m_ptexturehdr;
84 | studiohdr_t *m_panimhdr[32];
85 |
86 | vec4_t m_adj; // FIX: non persistant, make static
87 |
88 | void CalcBoneAdj( void );
89 | void CalcBoneQuaternion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *q );
90 | void CalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *pos );
91 | void CalcRotations ( vec3_t *pos, vec4_t *q, mstudioseqdesc_t *pseqdesc, mstudioanim_t *panim, float f );
92 | mstudioanim_t *GetAnim( mstudioseqdesc_t *pseqdesc );
93 | void SlerpBones( vec4_t q1[], vec3_t pos1[], vec4_t q2[], vec3_t pos2[], float s );
94 | void SetUpBones ( void );
95 |
96 | void DrawPoints( void );
97 |
98 | void Lighting (float *lv, int bone, int flags, vec3_t normal);
99 | void Chrome (int *chrome, int bone, vec3_t normal);
100 |
101 | void SetupLighting( void );
102 |
103 | void SetupModel ( int bodypart );
104 |
105 | static bool isCSOExternalTexture(const mstudiotexture_t &ptexture);
106 | };
107 |
108 |
109 |
110 | extern vec3_t g_vright; // needs to be set to viewer's right in order for chrome to work
111 | extern float g_lambert; // modifier for pseudo-hemispherical lighting
112 | extern StudioModel g_studioModel;
113 |
114 |
115 |
116 | #endif // INCLUDED_STUDIOMODEL
--------------------------------------------------------------------------------
/src/m/TGAlib.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // TGA lib for cocos2d-iphone
3 | //
4 | // sources from: http://www.lighthouse3d.com/opengl/terrain/index.php3?tgasource
5 | //
6 | // TGA RLE compression support by Ernesto Corvi
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include "TGAlib.h"
13 |
14 | void tgaLoadRLEImageData(FILE *file, tImageTGA *info);
15 | void tgaFlipImage( tImageTGA *info );
16 |
17 | // load the image header fields. We only keep those that matter!
18 | void tgaLoadHeader(FILE *file, tImageTGA *info) {
19 | unsigned char cGarbage;
20 | short int iGarbage;
21 |
22 | fread(&cGarbage, sizeof(unsigned char), 1, file);
23 | fread(&cGarbage, sizeof(unsigned char), 1, file);
24 |
25 | // type must be 2 or 3
26 | fread(&info->type, sizeof(unsigned char), 1, file);
27 |
28 | fread(&iGarbage, sizeof(short int), 1, file);
29 | fread(&iGarbage, sizeof(short int), 1, file);
30 | fread(&cGarbage, sizeof(unsigned char), 1, file);
31 | fread(&iGarbage, sizeof(short int), 1, file);
32 | fread(&iGarbage, sizeof(short int), 1, file);
33 |
34 | fread(&info->width, sizeof(short int), 1, file);
35 | fread(&info->height, sizeof(short int), 1, file);
36 | fread(&info->pixelDepth, sizeof(unsigned char), 1, file);
37 |
38 | fread(&cGarbage, sizeof(unsigned char), 1, file);
39 |
40 | info->flipped = 0;
41 | if ( cGarbage & 0x20 ) info->flipped = 1;
42 | }
43 |
44 | // loads the image pixels. You shouldn't call this function directly
45 | void tgaLoadImageData(FILE *file, tImageTGA *info) {
46 |
47 | int mode,total,i;
48 | unsigned char aux;
49 |
50 | // mode equal the number of components for each pixel
51 | mode = info->pixelDepth / 8;
52 | // total is the number of unsigned chars we'll have to read
53 | total = info->height * info->width * mode;
54 |
55 | fread(info->imageData,sizeof(unsigned char),total,file);
56 |
57 | // mode=3 or 4 implies that the image is RGB(A). However TGA
58 | // stores it as BGR(A) so we'll have to swap R and B.
59 | if (mode >= 3)
60 | for (i=0; i < total; i+= mode) {
61 | aux = info->imageData[i];
62 | info->imageData[i] = info->imageData[i+2];
63 | info->imageData[i+2] = aux;
64 | }
65 | }
66 |
67 | // loads the RLE encoded image pixels. You shouldn't call this function directly
68 | void tgaLoadRLEImageData(FILE *file, tImageTGA *info)
69 | {
70 | unsigned int mode,total,i, index = 0;
71 | unsigned char aux[4], runlength = 0;
72 | unsigned int skip = 0, flag = 0;
73 |
74 | // mode equal the number of components for each pixel
75 | mode = info->pixelDepth / 8;
76 | // total is the number of unsigned chars we'll have to read
77 | total = info->height * info->width;
78 |
79 | for( i = 0; i < total; i++ )
80 | {
81 | // if we have a run length pending, run it
82 | if ( runlength != 0 )
83 | {
84 | // we do, update the run length count
85 | runlength--;
86 | skip = (flag != 0);
87 | }
88 | else
89 | {
90 | // otherwise, read in the run length token
91 | if ( fread(&runlength,sizeof(unsigned char),1,file) != 1 )
92 | return;
93 |
94 | // see if it's a RLE encoded sequence
95 | flag = runlength & 0x80;
96 | if ( flag ) runlength -= 128;
97 | skip = 0;
98 | }
99 |
100 | // do we need to skip reading this pixel?
101 | if ( !skip )
102 | {
103 | // no, read in the pixel data
104 | if ( fread(aux,sizeof(unsigned char),mode,file) != mode )
105 | return;
106 |
107 | // mode=3 or 4 implies that the image is RGB(A). However TGA
108 | // stores it as BGR(A) so we'll have to swap R and B.
109 | if ( mode >= 3 )
110 | {
111 | unsigned char tmp;
112 |
113 | tmp = aux[0];
114 | aux[0] = aux[2];
115 | aux[2] = tmp;
116 | }
117 | }
118 |
119 | // add the pixel to our image
120 | memcpy(&info->imageData[index], aux, mode);
121 | index += mode;
122 | }
123 | }
124 |
125 | void tgaFlipImage( tImageTGA *info )
126 | {
127 | // mode equal the number of components for each pixel
128 | int mode = info->pixelDepth / 8;
129 | int rowbytes = info->width*mode;
130 | unsigned char *row = (unsigned char *)malloc(rowbytes);
131 | int y;
132 |
133 | if (row == NULL) return;
134 |
135 | for( y = 0; y < (info->height/2); y++ )
136 | {
137 | memcpy(row, &info->imageData[y*rowbytes],rowbytes);
138 | memcpy(&info->imageData[y*rowbytes], &info->imageData[(info->height-(y+1))*rowbytes], rowbytes);
139 | memcpy(&info->imageData[(info->height-(y+1))*rowbytes], row, rowbytes);
140 | }
141 |
142 | free(row);
143 | info->flipped = 0;
144 | }
145 |
146 | // this is the function to call when we want to load an image
147 | tImageTGA * tgaLoad(const char *filename) {
148 |
149 | FILE *file;
150 | tImageTGA *info;
151 | int mode,total;
152 |
153 | // allocate memory for the info struct and check!
154 | info = (tImageTGA *)malloc(sizeof(tImageTGA));
155 | if (info == NULL)
156 | return(NULL);
157 |
158 |
159 | // open the file for reading (binary mode)
160 | file = fopen(filename, "rb");
161 | if (file == NULL) {
162 | info->status = TGA_ERROR_FILE_OPEN;
163 | return(info);
164 | }
165 |
166 | // load the header
167 | tgaLoadHeader(file,info);
168 |
169 | // check for errors when loading the header
170 | if (ferror(file)) {
171 | info->status = TGA_ERROR_READING_FILE;
172 | fclose(file);
173 | return(info);
174 | }
175 |
176 | // check if the image is color indexed
177 | if (info->type == 1) {
178 | info->status = TGA_ERROR_INDEXED_COLOR;
179 | fclose(file);
180 | return(info);
181 | }
182 | // check for other types (compressed images)
183 | if ((info->type != 2) && (info->type !=3) && (info->type !=10) ) {
184 | info->status = TGA_ERROR_COMPRESSED_FILE;
185 | fclose(file);
186 | return(info);
187 | }
188 |
189 | // mode equals the number of image components
190 | mode = info->pixelDepth / 8;
191 | // total is the number of unsigned chars to read
192 | total = info->height * info->width * mode;
193 | // allocate memory for image pixels
194 | info->imageData = (unsigned char *)malloc(sizeof(unsigned char) *
195 | total);
196 |
197 | // check to make sure we have the memory required
198 | if (info->imageData == NULL) {
199 | info->status = TGA_ERROR_MEMORY;
200 | fclose(file);
201 | return(info);
202 | }
203 | // finally load the image pixels
204 | if ( info->type == 10 )
205 | tgaLoadRLEImageData(file, info);
206 | else
207 | tgaLoadImageData(file,info);
208 |
209 | // check for errors when reading the pixels
210 | if (ferror(file)) {
211 | info->status = TGA_ERROR_READING_FILE;
212 | fclose(file);
213 | return(info);
214 | }
215 | fclose(file);
216 | info->status = TGA_OK;
217 |
218 | if ( info->flipped )
219 | {
220 | tgaFlipImage( info );
221 | if ( info->flipped ) info->status = TGA_ERROR_MEMORY;
222 | }
223 |
224 | return(info);
225 | }
226 |
227 | // converts RGB to greyscale
228 | void tgaRGBtogreyscale(tImageTGA *info) {
229 |
230 | int mode,i,j;
231 |
232 | unsigned char *newImageData;
233 |
234 | // if the image is already greyscale do nothing
235 | if (info->pixelDepth == 8)
236 | return;
237 |
238 | // compute the number of actual components
239 | mode = info->pixelDepth / 8;
240 |
241 | // allocate an array for the new image data
242 | newImageData = (unsigned char *)malloc(sizeof(unsigned char) *
243 | info->height * info->width);
244 | if (newImageData == NULL) {
245 | return;
246 | }
247 |
248 | // convert pixels: greyscale = o.30 * R + 0.59 * G + 0.11 * B
249 | for (i = 0,j = 0; j < info->width * info->height; i +=mode, j++)
250 | newImageData[j] =
251 | (unsigned char)(0.30 * info->imageData[i] +
252 | 0.59 * info->imageData[i+1] +
253 | 0.11 * info->imageData[i+2]);
254 |
255 |
256 | //free old image data
257 | free(info->imageData);
258 |
259 | // reassign pixelDepth and type according to the new image type
260 | info->pixelDepth = 8;
261 | info->type = 3;
262 | // reassing imageData to the new array.
263 | info->imageData = newImageData;
264 | }
265 |
266 | // releases the memory used for the image
267 | void tgaDestroy(tImageTGA *info) {
268 |
269 | if (info != NULL) {
270 | if (info->imageData != NULL)
271 | free(info->imageData);
272 | free(info);
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/src/m/TGAlib.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGA lib for cocos2d-iphone
3 | //
4 | // sources from: http://www.lighthouse3d.com/opengl/terrain/index.php3?tgasource
5 | //
6 |
7 | //#ifndef TGA_LIB
8 | //#define TGA_LIB
9 |
10 | /**
11 | @file
12 | TGA image support
13 | */
14 |
15 | enum {
16 | TGA_OK,
17 | TGA_ERROR_FILE_OPEN,
18 | TGA_ERROR_READING_FILE,
19 | TGA_ERROR_INDEXED_COLOR,
20 | TGA_ERROR_MEMORY,
21 | TGA_ERROR_COMPRESSED_FILE,
22 | };
23 |
24 | /** TGA format */
25 | typedef struct sImageTGA {
26 | int status;
27 | unsigned char type, pixelDepth;
28 |
29 | /** map width */
30 | short int width;
31 |
32 | /** map height */
33 | short int height;
34 |
35 | /** raw data */
36 | unsigned char *imageData;
37 | int flipped;
38 | } tImageTGA;
39 |
40 | /// load the image header fields. We only keep those that matter!
41 | void tgaLoadHeader(FILE *file, tImageTGA *info);
42 |
43 | /// loads the image pixels. You shouldn't call this function directly
44 | void tgaLoadImageData(FILE *file, tImageTGA *info);
45 |
46 | /// this is the function to call when we want to load an image
47 | tImageTGA * tgaLoad(const char *filename);
48 |
49 | // /converts RGB to greyscale
50 | void tgaRGBtogreyscale(tImageTGA *info);
51 |
52 | /// releases the memory used for the image
53 | void tgaDestroy(tImageTGA *info);
54 |
55 | //#endif // TGA_LIB
56 |
--------------------------------------------------------------------------------
/src/m/ViewerSettings.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Half-Life Model Viewer (c) 1999 by Mete Ciragan
3 | //
4 | // file: ViewerSettings.cpp
5 | // last modified: May 29 1999, Mete Ciragan
6 | // copyright: The programs and associated files contained in this
7 | // distribution were developed by Mete Ciragan. The programs
8 | // are not in the public domain, but they are freely
9 | // distributable without licensing fees. These programs are
10 | // provided without guarantee or warrantee expressed or
11 | // implied.
12 | //
13 | // version: 1.2
14 | //
15 | // email: mete@swissquake.ch
16 | // web: http://www.swissquake.ch/chumbalum-soft/
17 | //
18 | #include "ViewerSettings.h"
19 | #include
20 | #include
21 | #include
22 |
23 |
24 |
25 | ViewerSettings g_viewerSettings;
26 |
27 |
28 |
29 | void
30 | InitViewerSettings (void)
31 | {
32 | g_viewerSettings.rot[0] = -90.0f;
33 | g_viewerSettings.trans[2] = 50.0f;
34 | g_viewerSettings.renderMode = RM_TEXTURED;
35 | g_viewerSettings.transparency = 1.0f;
36 | g_viewerSettings.use3dfx = true;
37 |
38 | g_viewerSettings.yaw = 65.0f;
39 | g_viewerSettings.bgColor[0] = 0.5f; // r
40 | g_viewerSettings.bgColor[1] = 0.75f; // g
41 | g_viewerSettings.bgColor[2] = 0.75f; // b
42 | g_viewerSettings.bgColor[3] = 1.0f; // a
43 |
44 | g_viewerSettings.gColor[0] = 0.85f;
45 | g_viewerSettings.gColor[1] = 0.85f;
46 | g_viewerSettings.gColor[2] = 0.69f;
47 |
48 | g_viewerSettings.lColor[0] = 1.0f;
49 | g_viewerSettings.lColor[1] = 1.0f;
50 | g_viewerSettings.lColor[2] = 1.0f;
51 |
52 | g_viewerSettings.guColor[0] = 1.0f;
53 | g_viewerSettings.guColor[1] = 0.0f;
54 | g_viewerSettings.guColor[2] = 0.0f;
55 |
56 | g_viewerSettings.speedScale = 1.0f;
57 | g_viewerSettings.textureLimit = 256;
58 |
59 | g_viewerSettings.textureScale = 1.0f;
60 |
61 | g_viewerSettings.vieworiginmode = false;
62 | g_viewerSettings.viewfov = 90.0f;
63 | g_viewerSettings.viewaspect = 0.0f;
64 |
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/src/m/ViewerSettings.h:
--------------------------------------------------------------------------------
1 | //
2 | // Half-Life Model Viewer (c) 1999 by Mete Ciragan
3 | //
4 | // file: ViewerSettings.h
5 | // last modified: May 29 1999, Mete Ciragan
6 | // copyright: The programs and associated files contained in this
7 | // distribution were developed by Mete Ciragan. The programs
8 | // are not in the public domain, but they are freely
9 | // distributable without licensing fees. These programs are
10 | // provided without guarantee or warrantee expressed or
11 | // implied.
12 | //
13 | // version: 1.2
14 | //
15 | // email: mete@swissquake.ch
16 | // web: http://www.swissquake.ch/chumbalum-soft/
17 | //
18 | #ifndef INCLUDED_VIEWERSETTINGS
19 | #define INCLUDED_VIEWERSETTINGS
20 |
21 |
22 |
23 | enum // render modes
24 | {
25 | RM_WIREFRAME,
26 | RM_FLATSHADED,
27 | RM_SMOOTHSHADED,
28 | RM_TEXTURED
29 | };
30 |
31 |
32 |
33 | typedef struct
34 | {
35 | // model
36 | float rot[3];
37 | float trans[3];
38 | float rotOld[3];
39 | float transOld[3];
40 | float rotOld2[3];
41 | float transOld2[3];
42 |
43 | // render
44 | int renderMode;
45 | float transparency;
46 | bool showBackground;
47 | bool showGround;
48 | bool showHitBoxes;
49 | bool showBones;
50 | bool showTexture;
51 | bool showAttachments;
52 | bool showGuideLines;
53 | bool showCrosshair;
54 | bool showEyePosition;
55 | bool showUVMap;
56 | bool showUVMapOverlay;
57 | bool showWireframeOverlay;
58 | bool showSmoothLines;
59 | int texture;
60 | float textureScale;
61 | int skin;
62 | int mesh;
63 | int meshCount;
64 | bool mirror;
65 | bool useStencil; // if 3dfx fullscreen set false
66 | bool showAllMeshes;
67 |
68 | // animation
69 | int sequence;
70 | float speedScale;
71 |
72 | // bodyparts and bonecontrollers
73 | int submodels[32];
74 | float controllers[8];
75 |
76 | // fullscreen
77 | int width, height;
78 | bool use3dfx;
79 | bool cds;
80 |
81 | // colors
82 | float bgColor[4];
83 | float lColor[4];
84 | float gColor[4];
85 | float guColor[4];
86 |
87 | // Field of View
88 | float yaw;
89 |
90 | // misc
91 | int textureLimit;
92 | bool pause;
93 |
94 | // only used for fullscreen mode
95 | char modelFile[256];
96 | char backgroundTexFile[256];
97 | char groundTexFile[256];
98 |
99 | // QHLMV spec
100 | bool vieworiginmode;
101 | bool righthand;
102 | float vieworigintrans[3];
103 | float viewfov;
104 | float viewaspect;
105 | } ViewerSettings;
106 |
107 |
108 |
109 | extern ViewerSettings g_viewerSettings;
110 |
111 |
112 |
113 | #ifdef __cplusplus
114 | extern "C" {
115 | #endif
116 |
117 | void InitViewerSettings (void);
118 |
119 | #ifdef __cplusplus
120 | }
121 | #endif
122 |
123 |
124 |
125 | #endif // INCLUDED_VIEWERSETTINGS
126 |
--------------------------------------------------------------------------------
/src/m/bmpread.cpp:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * libbmpread - tiny, fast bitmap (.bmp) image file loader *
3 | * *
4 | * Copyright (C) 2005, 2012, 2016, 2018 Charles Lindsay *
5 | * *
6 | * This software is provided 'as-is', without any express or implied *
7 | * warranty. In no event will the authors be held liable for any damages *
8 | * arising from the use of this software. *
9 | * *
10 | * Permission is granted to anyone to use this software for any purpose, *
11 | * including commercial applications, and to alter it and redistribute it *
12 | * freely, subject to the following restrictions: *
13 | * *
14 | * 1. The origin of this software must not be misrepresented; you must not *
15 | * claim that you wrote the original software. If you use this software *
16 | * in a product, an acknowledgment in the product documentation would be *
17 | * appreciated but is not required. *
18 | * 2. Altered source versions must be plainly marked as such, and must not be *
19 | * misrepresented as being the original software. *
20 | * 3. This notice may not be removed or altered from any source distribution. *
21 | ******************************************************************************/
22 |
23 |
24 | /* bmpread.c
25 | * version 3.0
26 | * 2018-02-02
27 | */
28 |
29 |
30 | #include "bmpread.h"
31 |
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | /* If your compiler doesn't come with stdint.h, which is technically a C99
39 | * feature, see . There are 3rd party
40 | * solutions to this problem, which you should be able to find with a little
41 | * searching. Alternately, just define the following types yourself: uint8_t,
42 | * uint16_t, uint32_t, and int32_t.
43 | */
44 | #include
45 |
46 | /* This code makes a number of assumptions about a byte being 8 bits, which is
47 | * technically not required by the C spec(s). It's likely that not a whole lot
48 | * here would need to change if CHAR_BIT != 8, but I haven't taken the time to
49 | * figure out exactly what those changes would be.
50 | */
51 | #if CHAR_BIT != 8
52 | #error "libbmpread requires CHAR_BIT == 8"
53 | #endif
54 |
55 |
56 | /* Default value for alpha when none is present in the file. */
57 | #define BMPREAD_DEFAULT_ALPHA 255
58 |
59 | /* I've tried to make every effort to remove the possibility of undefined
60 | * behavior and prevent related errors where maliciously crafted files could
61 | * lead to buffer overflows or the like. To that end, we'll start with some
62 | * functions that check various operations for behaving as expected. This one
63 | * returns nonzero if the two size_ts can be added without wrapping, or 0 if
64 | * the result would wrap.
65 | */
66 | static int CanAdd(size_t a, size_t b)
67 | {
68 | return a <= SIZE_MAX - b;
69 | }
70 |
71 | /* Returns nonzero if the two size_ts can be multiplied without wrapping, or 0
72 | * if the result would wrap. b must not be 0 (we don't even check here since
73 | * everything we pass in will have been checked before).
74 | */
75 | static int CanMultiply(size_t a, size_t b)
76 | {
77 | return a <= SIZE_MAX / b;
78 | }
79 |
80 | /* Returns nonzero if the uint32_t can be converted to a size_t without losing
81 | * data, which is always the case on 32-bit systems and higher, or 0 if such a
82 | * conversion would lose data, as could happen on 16-bit systems.
83 | */
84 | static int CanMakeSizeT(uint32_t x)
85 | {
86 | /* The preprocessor guard is there to prevent a warning about the condition
87 | * inside being true by definition on systems where size_t is at least 32
88 | * bits. I'm relying on C's integer promotion rules to make this all safe.
89 | * I *think* it works as intended here (either way, typecasts don't really
90 | * help clarify things, so I've gone without).
91 | */
92 | #if UINT32_MAX > SIZE_MAX
93 | if(x > SIZE_MAX) return 0;
94 | #endif
95 |
96 | (void)x; /* Sometimes unused; this prevents a pedantic warning. */
97 | return 1;
98 | }
99 |
100 | /* Returns nonzero if the uint32_t can be converted to a long without losing
101 | * data, or 0 if the conversion would lose data.
102 | */
103 | static int CanMakeLong(uint32_t x)
104 | {
105 | #if UINT32_MAX > LONG_MAX
106 | if(x > LONG_MAX) return 0;
107 | #endif
108 |
109 | (void)x; /* Sometimes unused. */
110 | return 1;
111 | }
112 |
113 | /* Returns nonzero if the int32_t can be negated properly. INT32_MIN doesn't
114 | * work because its positive value isn't representable inside an int32_t (given
115 | * two's complement).
116 | */
117 | static int CanNegate(int32_t x)
118 | {
119 | return x != INT32_MIN;
120 | }
121 |
122 | /* Reads up to 4 little-endian bytes from fp and stores the result in the
123 | * uint32_t pointed to by dest in the host's byte order. Returns 0 on EOF or
124 | * nonzero on success.
125 | */
126 | static int ReadLittleBytes(uint32_t * dest, int bytes, FILE * fp)
127 | {
128 | uint32_t shift = 0;
129 |
130 | *dest = 0;
131 |
132 | while(bytes--)
133 | {
134 | int byte;
135 | if((byte = fgetc(fp)) == EOF) return 0;
136 |
137 | *dest += (uint32_t)byte << shift;
138 | shift += 8;
139 | }
140 |
141 | return 1;
142 | }
143 |
144 | /* Reads a little-endian uint32_t from fp and stores the result in *dest in the
145 | * host's byte order. Returns 0 on EOF or nonzero on success.
146 | */
147 | #define ReadLittleUint32(dest, fp) ReadLittleBytes(dest, 4, fp)
148 |
149 | /* Reads a little-endian int32_t from fp and stores the result in *dest in the
150 | * host's byte order. Returns 0 on EOF or nonzero on success.
151 | */
152 | static int ReadLittleInt32(int32_t * dest, FILE * fp)
153 | {
154 | /* I *believe* casting unsigned -> signed is implementation-defined when
155 | * the unsigned value is out of range for the signed type, which would be
156 | * the case for any negative number we've just read out of the file into a
157 | * uint. This is a portable way to "reinterpret" the bits as signed
158 | * without running into undefined/implementation-defined behavior. I
159 | * think.
160 | */
161 | union int32_signedness_swap
162 | {
163 | uint32_t uint32;
164 | int32_t int32;
165 |
166 | } t;
167 |
168 | if(!ReadLittleBytes(&t.uint32, 4, fp)) return 0;
169 | *dest = t.int32;
170 | return 1;
171 | }
172 |
173 | /* Reads a little-endian uint16_t from fp and stores the result in *dest in the
174 | * host's byte order. Returns 0 on EOF or nonzero n success.
175 | */
176 | static int ReadLittleUint16(uint16_t * dest, FILE * fp)
177 | {
178 | uint32_t t;
179 | if(!ReadLittleBytes(&t, 2, fp)) return 0;
180 | *dest = (uint16_t)t;
181 | return 1;
182 | }
183 |
184 | /* Reads a uint8_t from fp and stores the result in *dest. Returns 0 on EOF or
185 | * nonzero on success.
186 | */
187 | static int ReadUint8(uint8_t * dest, FILE * fp)
188 | {
189 | int byte;
190 | if((byte = fgetc(fp)) == EOF) return 0;
191 | *dest = (uint8_t)byte;
192 | return 1;
193 | }
194 |
195 | /* Bitmap file header, including magic bytes.
196 | */
197 | typedef struct bmp_header
198 | {
199 | uint8_t magic[2]; /* Magic bytes 'B' and 'M'. */
200 | uint32_t file_size; /* Size of whole file. */
201 | uint32_t unused; /* Should be 0. */
202 | uint32_t data_offset; /* Offset from beginning of file to bitmap data. */
203 |
204 | } bmp_header;
205 |
206 | /* Reads a bitmap header from fp into header. Returns 0 on EOF or invalid
207 | * header, or nonzero on success.
208 | */
209 | static int ReadHeader(bmp_header * header, FILE * fp)
210 | {
211 | if(!ReadUint8(&header->magic[0], fp)) return 0;
212 | if(!ReadUint8(&header->magic[1], fp)) return 0;
213 |
214 | /* If it doesn't look like a bitmap header, don't even bother. */
215 | if(header->magic[0] != 0x42 /* 'B' */) return 0;
216 | if(header->magic[1] != 0x4d /* 'M' */) return 0;
217 |
218 | if(!ReadLittleUint32(&header->file_size, fp)) return 0;
219 | if(!ReadLittleUint32(&header->unused, fp)) return 0;
220 | if(!ReadLittleUint32(&header->data_offset, fp)) return 0;
221 |
222 | return 1;
223 | }
224 |
225 | /* How many bytes in the file are occupied by a header, by definition in the
226 | * spec. Note that even though our definition logically matches the spec's, C
227 | * struct padding/packing rules mean it might not be the same as
228 | * sizeof(bmp_header).
229 | */
230 | #define BMP_HEADER_SIZE 14
231 |
232 | /* Bitmap info: comes immediately after the header and describes the image.
233 | */
234 | typedef struct bmp_info
235 | {
236 | uint32_t info_size; /* Size of info struct (> sizeof(bmp_info)). */
237 | int32_t width; /* Width of image. */
238 | int32_t height; /* Height (< 0 means right-side up). */
239 | uint16_t planes; /* Planes (should be 1). */
240 | uint16_t bits; /* Number of bits (1, 4, 8, 16, 24, or 32). */
241 | uint32_t compression; /* See COMPRESSION_* values below. */
242 | uint32_t unused0[3]; /* We don't care about these fields. */
243 | uint32_t colors; /* How many colors in the palette, 0 = 1<info_size, fp)) return 0;
278 |
279 | /* Older formats might not have all the fields we require, so this check
280 | * comes first.
281 | */
282 | if(info->info_size < MIN_INFO_SIZE) return 0;
283 |
284 | if(!ReadLittleInt32( &info->width, fp)) return 0;
285 | if(!ReadLittleInt32( &info->height, fp)) return 0;
286 | if(!ReadLittleUint16(&info->planes, fp)) return 0;
287 | if(!ReadLittleUint16(&info->bits, fp)) return 0;
288 | if(!ReadLittleUint32(&info->compression, fp)) return 0;
289 | if(!ReadLittleUint32(&info->unused0[0], fp)) return 0;
290 | if(!ReadLittleUint32(&info->unused0[1], fp)) return 0;
291 | if(!ReadLittleUint32(&info->unused0[2], fp)) return 0;
292 | if(!ReadLittleUint32(&info->colors, fp)) return 0;
293 | if(!ReadLittleUint32(&info->unused1, fp)) return 0;
294 |
295 | /* We don't bother to even try to read bitmasks if they aren't needed,
296 | * since they won't be present in Windows 3 format bitmap files.
297 | */
298 | if(info->compression == COMPRESSION_BITFIELDS)
299 | {
300 | /* Reject Windows NT format files with bitfields, since we don't
301 | * support them, and their masks aren't part of the info header anyway.
302 | */
303 | if(info->info_size == BMP3_INFO_SIZE) return 0;
304 |
305 | if(!ReadLittleUint32(&info->masks[0], fp)) return 0;
306 | if(!ReadLittleUint32(&info->masks[1], fp)) return 0;
307 | if(!ReadLittleUint32(&info->masks[2], fp)) return 0;
308 | if(!ReadLittleUint32(&info->masks[3], fp)) return 0;
309 | }
310 |
311 | return 1;
312 | }
313 |
314 | /* Bitfields for 16- and 32-bit files. We track the first set bit (rightmost
315 | * being 0) and how many bits it spans.
316 | */
317 | typedef struct bitfield
318 | {
319 | uint32_t start;
320 | uint32_t span;
321 |
322 | } bitfield;
323 |
324 | /* Applies a bitfield mask to a value, x.
325 | */
326 | #define ApplyBitfield(x, bitfield) \
327 | (((x) >> (bitfield).start) & ((UINT32_C(1) << (bitfield).span) - 1))
328 |
329 | /* Turns a single mask component into a bitfield. Returns 0 if the bitmask was
330 | * invalid, or nonzero if it's ok. Span of 0 means the bitmask was absent.
331 | */
332 | static int ParseBitfield(bitfield * field, uint32_t mask)
333 | {
334 | uint32_t bit;
335 | for(bit = 0; bit < 32 && !(mask & (UINT32_C(1) << bit)); bit++)
336 | ;
337 |
338 | if(bit >= 32)
339 | {
340 | /* Absent bitmasks are valid. */
341 | field->start = field->span = 0;
342 | return 1;
343 | }
344 |
345 | field->start = bit;
346 | for(; bit < 32 && (mask & (UINT32_C(1) << bit)); bit++)
347 | ;
348 | field->span = bit - field->start;
349 |
350 | /* If there are more set bits, there was a gap, which is invalid. */
351 | if(bit < 32 && (mask & ~((UINT32_C(1) << bit) - 1))) return 0;
352 |
353 | return 1;
354 | }
355 |
356 | /* A single color entry in the palette, in file order (BGR + one unused byte).
357 | */
358 | typedef struct bmp_color
359 | {
360 | uint8_t blue;
361 | uint8_t green;
362 | uint8_t red;
363 | uint8_t unused;
364 |
365 | } bmp_color;
366 |
367 | /* How many bytes in the file are occupied by a palette entry, by definition in
368 | * the spec (and again note that it might not be the same as
369 | * sizeof(bmp_color), even if we match).
370 | */
371 | #define BMP_COLOR_SIZE 4
372 |
373 | /* Reads the given number of colors from fp into the palette array. Returns 0
374 | * on EOF or nonzero on success.
375 | */
376 | static int ReadPalette(bmp_color * palette, int colors, FILE * fp)
377 | {
378 | /* This isn't the guaranteed-fastest way to implement this, but it should
379 | * perform quite well in practice due to compiler optimization and stdio
380 | * input buffering. It's implemented this way because of how simple the
381 | * code is, while avoiding undefined and implementation-defined behavior or
382 | * allocating any memory. If you aren't averse to an extra allocation (or
383 | * using a chunk of the stack), it might be made faster while still
384 | * avoiding implementation-defined behavior by reading the entire palette
385 | * into one big buffer up front, then copying bytes into place.
386 | */
387 | int i;
388 | for(i = 0; i < colors; i++)
389 | {
390 | uint8_t components[BMP_COLOR_SIZE];
391 | if(fread(components, 1, sizeof(components), fp) != sizeof(components))
392 | return 0;
393 |
394 | palette[i].blue = components[0];
395 | palette[i].green = components[1];
396 | palette[i].red = components[2];
397 | palette[i].unused = components[3];
398 | }
399 | return 1;
400 | }
401 |
402 | /* Context shared between the below functions.
403 | */
404 | typedef struct read_context
405 | {
406 | unsigned int flags; /* Flags passed to bmpread. */
407 | FILE * fp; /* File pointer. */
408 | bmp_header header; /* Bitmap file header. */
409 | bmp_info info; /* Bitmap file info. */
410 | uint32_t headers_size; /* Total size of header + info. */
411 | uint32_t after_headers; /* Size of space for palette. */
412 | int32_t lines; /* How many scan lines (abs(height)). */
413 | size_t file_line_len; /* How many bytes each scan line is. */
414 | size_t out_channels; /* Output color channels (3, or 4=alpha). */
415 | size_t out_line_len; /* Bytes in each output line. */
416 | bitfield bitfields[4]; /* How to decode 16- and 32-bits. */
417 | bmp_color * palette; /* Enough entries for our bit depth. */
418 | uint8_t * file_data; /* A line of data in the file. */
419 | uint8_t * data_out; /* RGB(A) data output buffer. */
420 |
421 | } read_context;
422 |
423 | /* A sub-function to Validate() that handles the bitfields. Returns 0 on
424 | * invalid bitfields or nonzero on success. Note that we don't treat odd
425 | * bitmasks such as R8G8 or A1G1B1 as invalid, even though they may not load in
426 | * most other loaders.
427 | */
428 | static int ValidateBitfields(read_context * p_ctx)
429 | {
430 | bitfield * bf = p_ctx->bitfields;
431 |
432 | uint32_t total_mask = 0;
433 | bitfield total_field;
434 |
435 | int i;
436 |
437 | if(p_ctx->info.compression != COMPRESSION_BITFIELDS)
438 | return 1;
439 |
440 | for(i = 0; i < 4; i++)
441 | {
442 | /* No overlapping masks. */
443 | if(total_mask & p_ctx->info.masks[i]) return 0;
444 | total_mask |= p_ctx->info.masks[i];
445 |
446 | if(!ParseBitfield(&bf[i], p_ctx->info.masks[i])) return 0;
447 |
448 | /* Make sure we fit in our bit size. */
449 | if(bf[i].start + bf[i].span > p_ctx->info.bits) return 0;
450 | }
451 |
452 | if(!total_mask) return 0;
453 |
454 | /* Check for contiguous-ity between fields, too. */
455 | if(!ParseBitfield(&total_field, total_mask)) return 0;
456 |
457 | return 1;
458 | }
459 |
460 | /* A sub-function to Validate() that handles the palette. Returns 0 on EOF or
461 | * invalid palette, or nonzero on success.
462 | */
463 | static int ValidateAndReadPalette(read_context * p_ctx)
464 | {
465 | uint32_t colors = UINT32_C(1) << p_ctx->info.bits;
466 | uint32_t file_colors = p_ctx->info.colors;
467 |
468 | if(p_ctx->info.bits > 8)
469 | return 1;
470 |
471 | if(file_colors > colors) return 0;
472 | if(!file_colors)
473 | file_colors = colors;
474 |
475 | /* Make sure we actually have space in the file for all the colors. */
476 | if(p_ctx->after_headers / BMP_COLOR_SIZE < file_colors) return 0;
477 |
478 | /* We always allocate a full palette even if the file only claims to
479 | * contain a smaller number, so we don't have to check for out of bound
480 | * color lookups. Not sure what the desired behavior is, but loading the
481 | * image anyway and treating OOB colors as black seems ok to me. 0-fill so
482 | * lookups beyond the file's palette get set to black.
483 | */
484 | if(!(p_ctx->palette = (bmp_color *)
485 | calloc(colors, sizeof(p_ctx->palette[0])))) return 0;
486 |
487 | if(!CanMakeLong(p_ctx->headers_size)) return 0;
488 | if(fseek(p_ctx->fp, p_ctx->headers_size, SEEK_SET)) return 0;
489 | if(!ReadPalette(p_ctx->palette, file_colors, p_ctx->fp)) return 0;
490 |
491 | return 1;
492 | }
493 |
494 | /* Returns whether a non-negative integer is a power of 2.
495 | */
496 | static int IsPowerOf2(uint32_t x)
497 | {
498 | while(x)
499 | {
500 | /* When we find a bit, return whether no other bits are set. */
501 | if(x & 1)
502 | return !(x & ~UINT32_C(1));
503 | x = x >> 1;
504 | }
505 |
506 | /* 0, the only value for x which lands us here, isn't a power of 2. */
507 | return 0;
508 | }
509 |
510 | /* Returns the byte length of a scan line padded as necessary to be divisible
511 | * by four. For example, 3 pixels wide at 24 bpp would yield 12 (3 pixels * 3
512 | * bytes each = 9 bytes, padded by 3 to the next multiple of 4). bpp is *bits*
513 | * per pixel, not bytes. Returns 0 in case of overflow.
514 | */
515 | static size_t GetLineLength(size_t width, size_t bpp)
516 | {
517 | size_t bits = width * bpp;
518 | size_t pad_bits = (32 - (bits & 0x1f)) & 0x1f; /* x & 0x1f == x % 32 */
519 |
520 | /* Check for overflow, in both the above multiplication and the below
521 | * addition. It's well defined to do this in any order relative to the
522 | * operations themselves (since size_t is unsigned), so we combine the
523 | * checks into one if. bpp has been checked for being nonzero elsewhere.
524 | */
525 | if(!CanMultiply(width, bpp) || !CanAdd(bits, pad_bits)) return 0;
526 |
527 | /* Convert to bytes. */
528 | return (bits + pad_bits) / 8;
529 | }
530 |
531 | /* Reads and validates the bitmap header metadata from the context's file
532 | * object. Assumes the file pointer is at the start of the file. Returns 1 if
533 | * ok or 0 if error or invalid file.
534 | */
535 | static int Validate(read_context * p_ctx)
536 | {
537 | if(!ReadHeader(&p_ctx->header, p_ctx->fp)) return 0;
538 | if(!ReadInfo( &p_ctx->info, p_ctx->fp)) return 0;
539 |
540 | if(p_ctx->info.info_size > UINT32_MAX - BMP_HEADER_SIZE) return 0;
541 | p_ctx->headers_size = BMP_HEADER_SIZE + p_ctx->info.info_size;
542 |
543 | if(p_ctx->header.data_offset < p_ctx->headers_size) return 0;
544 | p_ctx->after_headers = p_ctx->header.data_offset - p_ctx->headers_size;
545 |
546 | if(p_ctx->info.width <= 0 || p_ctx->info.height == 0) return 0;
547 |
548 | if(!CanMakeSizeT(p_ctx->info.width)) return 0;
549 | if(!CanNegate( p_ctx->info.height)) return 0;
550 | p_ctx->lines = ((p_ctx->info.height < 0) ?
551 | -p_ctx->info.height :
552 | p_ctx->info.height);
553 |
554 | if(!(p_ctx->flags & BMPREAD_ANY_SIZE))
555 | {
556 | /* Both of these values have just been checked against being negative,
557 | * and thus it's safe to pass them on as uint32_t.
558 | */
559 | if(!IsPowerOf2(p_ctx->info.width)) return 0;
560 | if(!IsPowerOf2(p_ctx->lines)) return 0;
561 | }
562 |
563 | switch(p_ctx->info.compression)
564 | {
565 | case COMPRESSION_NONE:
566 | if(p_ctx->info.bits != 1 && p_ctx->info.bits != 4 &&
567 | p_ctx->info.bits != 8 && p_ctx->info.bits != 24) return 0;
568 | break;
569 |
570 | case COMPRESSION_BITFIELDS:
571 | if(p_ctx->info.bits != 16 && p_ctx->info.bits != 32) return 0;
572 | break;
573 |
574 | default: /* No compression supported yet (TODO: handle RLE). */
575 | return 0;
576 | }
577 |
578 | p_ctx->file_line_len = GetLineLength(p_ctx->info.width, p_ctx->info.bits);
579 | if(p_ctx->file_line_len == 0) return 0;
580 |
581 | p_ctx->out_channels = ((p_ctx->flags & BMPREAD_ALPHA) ? 4 : 3);
582 |
583 | /* This check happens outside the following if, where it would seem to
584 | * belong, because we make the same computation again in the future.
585 | */
586 | if(!CanMultiply(p_ctx->info.width, p_ctx->out_channels)) return 0;
587 |
588 | if(p_ctx->flags & BMPREAD_BYTE_ALIGN)
589 | p_ctx->out_line_len = (size_t)p_ctx->info.width * p_ctx->out_channels;
590 | else
591 | {
592 | p_ctx->out_line_len = GetLineLength(p_ctx->info.width,
593 | p_ctx->out_channels * 8);
594 | if(p_ctx->out_line_len == 0) return 0;
595 | }
596 |
597 | if(!ValidateBitfields(p_ctx)) return 0;
598 | if(!ValidateAndReadPalette(p_ctx)) return 0;
599 |
600 | /* Set things up for decoding. */
601 | if(!(p_ctx->file_data = (uint8_t *)malloc(p_ctx->file_line_len))) return 0;
602 |
603 | if(!CanMakeSizeT(p_ctx->lines)) return 0;
604 | if(!CanMultiply( p_ctx->lines, p_ctx->out_line_len)) return 0;
605 | if(!(p_ctx->data_out = (uint8_t *)
606 | malloc((size_t)p_ctx->lines * p_ctx->out_line_len))) return 0;
607 |
608 | return 1;
609 | }
610 |
611 | /* Evenly distribute a value that spans a given number of bits into 8 bits.
612 | */
613 | static uint8_t Make8Bits(uint32_t value, uint32_t bitspan)
614 | {
615 | uint32_t output = 0;
616 |
617 | if(bitspan == 8)
618 | return (uint8_t)value;
619 | if(bitspan > 8)
620 | return (uint8_t)(value >> (bitspan - 8));
621 |
622 | value <<= (8 - bitspan); /* Shift it up into the most significant bits. */
623 | while(value)
624 | {
625 | /* Repeat the bit pattern down into the least significant bits. This
626 | * gives an even distribution when extrapolating from [0, 2^bitspan-1]
627 | * into [0, 2^8-1], and avoids both floating point and awkward integer
628 | * multiplication. Unfortunately, because we don't enforce a whitelist
629 | * of bit patterns we support and can hard-code for, it necessitates a
630 | * loop. I believe this is a fairly efficient way to express the idea,
631 | * but it'd still be nice if the compiler could optimize this whole
632 | * function heavily, since it's called in a tight decode loop.
633 | */
634 | output |= value;
635 | value >>= bitspan;
636 | }
637 |
638 | return (uint8_t)output;
639 | }
640 |
641 | /* Reads four bytes out of a memory buffer and converts it to a uint32_t.
642 | */
643 | #define LoadLittleUint32(buf) (((uint32_t)(buf)[0] ) + \
644 | ((uint32_t)(buf)[1] << 8) + \
645 | ((uint32_t)(buf)[2] << 16) + \
646 | ((uint32_t)(buf)[3] << 24))
647 |
648 | /* Decodes 32-bit bitmap data by applying bitmasks. The 16- and 32-bit
649 | * decoders could be made more efficient by whitelisting supported bit patterns
650 | * ahead of time and special-casing their decoding here, but this allows us to
651 | * support more bitmask patterns, and shouldn't be *too* inefficient in any
652 | * case.
653 | *
654 | * Takes a pointer to an output buffer scan line (p_out), a pointer to the end
655 | * of the *pixel data* of this scan line (p_out_end), a pointer to the source
656 | * scan line of file data (p_file), and our context.
657 | */
658 | static void Decode32(uint8_t * p_out,
659 | const uint8_t * p_out_end,
660 | const uint8_t * p_file,
661 | const read_context * p_ctx)
662 | {
663 | const bitfield * bf = p_ctx->bitfields;
664 |
665 | while(p_out < p_out_end)
666 | {
667 | uint32_t value = LoadLittleUint32(p_file);
668 |
669 | *p_out++ = Make8Bits(ApplyBitfield(value, bf[0]), bf[0].span);
670 | *p_out++ = Make8Bits(ApplyBitfield(value, bf[1]), bf[1].span);
671 | *p_out++ = Make8Bits(ApplyBitfield(value, bf[2]), bf[2].span);
672 | if(p_ctx->out_channels == 4)
673 | {
674 | if(bf[3].span)
675 | *p_out++ = Make8Bits(ApplyBitfield(value, bf[3]), bf[3].span);
676 | else
677 | *p_out++ = BMPREAD_DEFAULT_ALPHA;
678 | }
679 |
680 | p_file += 4;
681 | }
682 | }
683 |
684 | /* Decodes 24-bit bitmap data--basically just swaps the order of color
685 | * components.
686 | */
687 | static void Decode24(uint8_t * p_out,
688 | const uint8_t * p_out_end,
689 | const uint8_t * p_file,
690 | const read_context * p_ctx)
691 | {
692 | while(p_out < p_out_end)
693 | {
694 | *p_out++ = *(p_file + 2);
695 | *p_out++ = *(p_file + 1);
696 | *p_out++ = *(p_file );
697 | if(p_ctx->out_channels == 4)
698 | *p_out++ = BMPREAD_DEFAULT_ALPHA;
699 |
700 | p_file += 3;
701 | }
702 | }
703 |
704 | /* Reads two bytes out of a memory buffer and converts it to a uint16_t.
705 | */
706 | #define LoadLittleUint16(buf) (((uint16_t)(buf)[0] ) + \
707 | ((uint16_t)(buf)[1] << 8))
708 |
709 | /* Decodes 16-bit bitmap data by applying bitmasks.
710 | */
711 | static void Decode16(uint8_t * p_out,
712 | const uint8_t * p_out_end,
713 | const uint8_t * p_file,
714 | const read_context * p_ctx)
715 | {
716 | const bitfield * bf = p_ctx->bitfields;
717 |
718 | while(p_out < p_out_end)
719 | {
720 | uint16_t value = LoadLittleUint16(p_file);
721 |
722 | *p_out++ = Make8Bits(ApplyBitfield(value, bf[0]), bf[0].span);
723 | *p_out++ = Make8Bits(ApplyBitfield(value, bf[1]), bf[1].span);
724 | *p_out++ = Make8Bits(ApplyBitfield(value, bf[2]), bf[2].span);
725 | if(p_ctx->out_channels == 4)
726 | {
727 | if(bf[3].span)
728 | *p_out++ = Make8Bits(ApplyBitfield(value, bf[3]), bf[3].span);
729 | else
730 | *p_out++ = BMPREAD_DEFAULT_ALPHA;
731 | }
732 |
733 | p_file += 2;
734 | }
735 | }
736 |
737 | /* Decodes 8-bit bitmap data by looking colors up in the palette.
738 | */
739 | static void Decode8(uint8_t * p_out,
740 | const uint8_t * p_out_end,
741 | const uint8_t * p_file,
742 | const read_context * p_ctx)
743 | {
744 | while(p_out < p_out_end) {
745 | *p_out++ = p_ctx->palette[*p_file].red;
746 | *p_out++ = p_ctx->palette[*p_file].green;
747 | *p_out++ = p_ctx->palette[*p_file].blue;
748 | if(p_ctx->out_channels == 4)
749 | *p_out++ = BMPREAD_DEFAULT_ALPHA;
750 |
751 | p_file++;
752 | }
753 | }
754 |
755 | /* Decodes 4-bit bitmap data by looking colors up in the palette.
756 | */
757 | static void Decode4(uint8_t * p_out,
758 | const uint8_t * p_out_end,
759 | const uint8_t * p_file,
760 | const read_context * p_ctx)
761 | {
762 | while(p_out < p_out_end)
763 | {
764 | unsigned int lookup = (*p_file & 0xf0U) >> 4;
765 |
766 | *p_out++ = p_ctx->palette[lookup].red;
767 | *p_out++ = p_ctx->palette[lookup].green;
768 | *p_out++ = p_ctx->palette[lookup].blue;
769 | if(p_ctx->out_channels == 4)
770 | *p_out++ = BMPREAD_DEFAULT_ALPHA;
771 |
772 | if(p_out < p_out_end)
773 | {
774 | lookup = *p_file++ & 0x0fU;
775 |
776 | *p_out++ = p_ctx->palette[lookup].red;
777 | *p_out++ = p_ctx->palette[lookup].green;
778 | *p_out++ = p_ctx->palette[lookup].blue;
779 | if(p_ctx->out_channels == 4)
780 | *p_out++ = BMPREAD_DEFAULT_ALPHA;
781 | }
782 | }
783 | }
784 |
785 | /* Decodes 1-bit bitmap data by looking colors up in the two-color palette.
786 | */
787 | static void Decode1(uint8_t * p_out,
788 | const uint8_t * p_out_end,
789 | const uint8_t * p_file,
790 | const read_context * p_ctx)
791 | {
792 | while(p_out < p_out_end)
793 | {
794 | unsigned int bit;
795 | for(bit = 0; bit < 8 && p_out < p_out_end; bit++)
796 | {
797 | unsigned int lookup = (*p_file >> (7 - bit)) & 1;
798 |
799 | *p_out++ = p_ctx->palette[lookup].red;
800 | *p_out++ = p_ctx->palette[lookup].green;
801 | *p_out++ = p_ctx->palette[lookup].blue;
802 | if(p_ctx->out_channels == 4)
803 | *p_out++ = BMPREAD_DEFAULT_ALPHA;
804 | }
805 |
806 | p_file++;
807 | }
808 | }
809 |
810 | /* Selects an above decoder and runs it for each scan line of the file.
811 | * Returns 0 if there's an error or 1 if it's gravy.
812 | */
813 | static int Decode(read_context * p_ctx)
814 | {
815 | void (* decoder)(uint8_t *, const uint8_t *, const uint8_t *,
816 | const read_context *);
817 |
818 | uint8_t * p_out; /* Pointer to current scan line in output buffer. */
819 | uint8_t * p_out_end; /* End marker for output buffer. */
820 | uint8_t * p_line_end; /* Pointer to end of current scan line in output. */
821 |
822 | /* out_inc is an incrementor for p_out to advance it one scan line. I'm
823 | * not exactly sure what the correct type for it would be, perhaps ssize_t,
824 | * but that's not C standard. I went with ptrdiff_t because its value
825 | * will be equivalent to the difference between two pointers, whether it
826 | * was derived that way or not.
827 | */
828 | ptrdiff_t out_inc;
829 |
830 | /* Double check this won't overflow. Who knows, man. */
831 | #if SIZE_MAX > PTRDIFF_MAX
832 | if(p_ctx->out_line_len > PTRDIFF_MAX) return 0;
833 | #endif
834 | out_inc = p_ctx->out_line_len;
835 |
836 | if(!(p_ctx->info.height < 0) == !(p_ctx->flags & BMPREAD_TOP_DOWN))
837 | {
838 | /* We're keeping scan lines in order. These and subsequent operations
839 | * have all been checked earlier.
840 | */
841 | p_out = p_ctx->data_out;
842 | p_out_end = p_ctx->data_out +
843 | ((size_t)p_ctx->lines * p_ctx->out_line_len);
844 | }
845 | else /* We're reversing scan lines. */
846 | {
847 | /* TODO: I'm not 100% sure about the legality, purely C spec-wise, of
848 | * this subtraction.
849 | */
850 | p_out_end = p_ctx->data_out - p_ctx->out_line_len;
851 | p_out = p_ctx->data_out +
852 | (((size_t)p_ctx->lines - 1) * p_ctx->out_line_len);
853 |
854 | /* Always safe, given two's complement, since it was positive. */
855 | out_inc = -out_inc;
856 | }
857 |
858 | p_line_end = p_out + (size_t)p_ctx->info.width * p_ctx->out_channels;
859 |
860 | switch(p_ctx->info.bits)
861 | {
862 | case 32: decoder = Decode32; break;
863 | case 24: decoder = Decode24; break;
864 | case 16: decoder = Decode16; break;
865 | case 8: decoder = Decode8; break;
866 | case 4: decoder = Decode4; break;
867 | case 1: decoder = Decode1; break;
868 | default: return 0;
869 | }
870 |
871 | if(!CanMakeLong(p_ctx->header.data_offset)) return 0;
872 | if(fseek(p_ctx->fp, p_ctx->header.data_offset, SEEK_SET)) return 0;
873 |
874 | while(p_out != p_out_end &&
875 | fread(p_ctx->file_data, 1, p_ctx->file_line_len, p_ctx->fp) ==
876 | p_ctx->file_line_len)
877 | {
878 | decoder(p_out, p_line_end, p_ctx->file_data, p_ctx);
879 |
880 | p_out += out_inc;
881 | p_line_end += out_inc;
882 | }
883 |
884 | return (p_out == p_out_end);
885 | }
886 |
887 | /* Frees resources allocated by various functions along the way. Only frees
888 | * data_out if !leave_data_out (if the bitmap loads successfully, you want the
889 | * data to remain until THEY free it).
890 | */
891 | static void FreeContext(read_context * p_ctx, int leave_data_out)
892 | {
893 | if(p_ctx->fp)
894 | fclose(p_ctx->fp);
895 | if(p_ctx->palette)
896 | free(p_ctx->palette);
897 | if(p_ctx->file_data)
898 | free(p_ctx->file_data);
899 |
900 | if(!leave_data_out && p_ctx->data_out)
901 | free(p_ctx->data_out);
902 | }
903 |
904 | int bmpread(const char * bmp_file, unsigned int flags, bmpread_t * p_bmp_out)
905 | {
906 | int success = 0;
907 |
908 | read_context ctx;
909 | memset(&ctx, 0, sizeof(ctx));
910 |
911 | do
912 | {
913 | if(!bmp_file) break;
914 | if(!p_bmp_out) break;
915 | memset(p_bmp_out, 0, sizeof(*p_bmp_out));
916 |
917 | ctx.flags = flags;
918 |
919 | if(!(ctx.fp = fopen(bmp_file, "rb"))) break;
920 | if(!Validate(&ctx)) break;
921 | if(!Decode(&ctx)) break;
922 |
923 | /* Finally, make sure we can stuff these into ints. I feel like this
924 | * is slightly justified by how it keeps the header definition dead
925 | * simple (including, well, no #includes). I suppose this could also
926 | * be done way earlier and maybe save some disk reads, but I like
927 | * keeping the check with the code it's checking.
928 | */
929 | #if INT32_MAX > INT_MAX
930 | if(ctx.info.width > INT_MAX) break;
931 | if(ctx.lines > INT_MAX) break;
932 | #endif
933 |
934 | p_bmp_out->width = ctx.info.width;
935 | p_bmp_out->height = ctx.lines;
936 | p_bmp_out->flags = ctx.flags;
937 | p_bmp_out->data = ctx.data_out;
938 |
939 | success = 1;
940 | } while(0);
941 |
942 | FreeContext(&ctx, success);
943 |
944 | return success;
945 | }
946 |
947 | void bmpread_free(bmpread_t * p_bmp)
948 | {
949 | if(p_bmp)
950 | {
951 | if(p_bmp->data)
952 | free(p_bmp->data);
953 |
954 | memset(p_bmp, 0, sizeof(*p_bmp));
955 | }
956 | }
--------------------------------------------------------------------------------
/src/m/bmpread.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * libbmpread - tiny, fast bitmap (.bmp) image file loader *
3 | * *
4 | * Copyright (C) 2005, 2012, 2016, 2018 Charles Lindsay *
5 | * *
6 | * This software is provided 'as-is', without any express or implied *
7 | * warranty. In no event will the authors be held liable for any damages *
8 | * arising from the use of this software. *
9 | * *
10 | * Permission is granted to anyone to use this software for any purpose, *
11 | * including commercial applications, and to alter it and redistribute it *
12 | * freely, subject to the following restrictions: *
13 | * *
14 | * 1. The origin of this software must not be misrepresented; you must not *
15 | * claim that you wrote the original software. If you use this software *
16 | * in a product, an acknowledgment in the product documentation would be *
17 | * appreciated but is not required. *
18 | * 2. Altered source versions must be plainly marked as such, and must not be *
19 | * misrepresented as being the original software. *
20 | * 3. This notice may not be removed or altered from any source distribution. *
21 | ******************************************************************************/
22 |
23 |
24 | /* bmpread.h
25 | * version 3.0
26 | * 2018-02-02
27 | */
28 |
29 |
30 | #ifndef __bmpread_h__
31 | #define __bmpread_h__
32 |
33 | #ifdef __cplusplus
34 | extern "C"
35 | {
36 | #endif
37 |
38 |
39 | /* Flags for bmpread() and bmpread_t (see below). Combine with bitwise OR.
40 | */
41 |
42 | /* Output data as top line first (default is bottom line first). */
43 | #define BMPREAD_TOP_DOWN 1u
44 |
45 | /* Don't pad lines to span a multiple of four bytes (default does pad). */
46 | #define BMPREAD_BYTE_ALIGN 2u
47 |
48 | /* Allow loading of any size bitmap (default is bitmaps must be 2^n x 2^m). */
49 | #define BMPREAD_ANY_SIZE 4u
50 |
51 | /* Load and output an alpha channel (default is just color channels). */
52 | #define BMPREAD_ALPHA 8u
53 |
54 |
55 | /* The struct filled by bmpread(). Holds information about the image's pixels.
56 | */
57 | typedef struct bmpread_t
58 | {
59 | int width; /* Width in pixels. */
60 | int height; /* Height in pixels. */
61 |
62 | /* BMPREAD_* flags, defined above, combined with bitwise OR, that affect
63 | * the format of data. These are set to the flags passed to bmpread().
64 | */
65 | unsigned int flags;
66 |
67 | /* A buffer holding the pixel data of the image.
68 | *
69 | * By default, each pixel spans three bytes: the red, green, and blue color
70 | * components in that order. However, with BMPREAD_ALPHA set in flags,
71 | * each pixel spans four bytes: the red, green, blue, and alpha components
72 | * in that order.
73 | *
74 | * Pixels are ordered left to right sequentially. By default, the bottom
75 | * line comes first, proceeding upward. However, with BMPREAD_TOP_DOWN set
76 | * in flags, the top line comes first, proceeding downward instead.
77 | *
78 | * Lines by default must span a multiple of four bytes. If the image width
79 | * and pixel span don't yield a multiple of four (a non-issue for
80 | * BMPREAD_ALPHA with four bytes per pixel), the end of each line is padded
81 | * with up to three unused bytes to meet the requirement. For example,
82 | * each line of an image three pixels wide, loaded without BMPREAD_ALPHA,
83 | * will span 12 bytes (3 pixels * 3 (RGB) channels per pixel = 9, padded
84 | * with 3 bytes up to the next multiple of 4). However, this behavior is
85 | * disabled with BMPREAD_BYTE_ALIGN set in flags, in which case all lines
86 | * span exactly width * pixel_span bytes.
87 | */
88 | unsigned char * data;
89 |
90 | } bmpread_t;
91 |
92 |
93 | /* Loads the specified bitmap file from disk and fills out a bmpread_t struct
94 | * with data about it.
95 | *
96 | * Inputs:
97 | * bmp_file - The filename of the bitmap file to load.
98 | * flags - Any BMPREAD_* flags, defined above, combined with bitwise OR.
99 | * Specify 0 (or BMPREAD_ALPHA if you want an alpha channel) for
100 | * standard, OpenGL compliant behavior.
101 | * p_bmp_out - Pointer to a bmpread_t struct to fill with information. Its
102 | * contents on input are ignored. Must be freed with
103 | * bmpread_free() when no longer needed.
104 | *
105 | * Returns:
106 | * 0 if there's an error (file doesn't exist or is invalid, i/o error, etc.),
107 | * or nonzero if the file loaded ok.
108 | *
109 | * Notes:
110 | * The file must be a Windows 3 (not NT) or higher format bitmap file with any
111 | * valid bit depth (1, 4, 8, 16, 24, or 32), and must not be compressed (no
112 | * RLE).
113 | *
114 | * Default behavior is for bmpread() to return data in a format directly usable
115 | * by OpenGL texture functions, e.g. glTexImage2D, format GL_RGB (or GL_RGBA if
116 | * BMPREAD_ALPHA is in flags), type GL_UNSIGNED_BYTE. This implies a few
117 | * oddities:
118 | * - Lines are ordered bottom-first. To return data starting with the top
119 | * line like you might otherwise expect, pass BMPREAD_TOP_DOWN in flags.
120 | * - Lines are padded to span a multiple of four bytes. To return data with
121 | * no padding, pass BMPREAD_BYTE_ALIGN in flags.
122 | * - Images with a width or height that isn't a power of 2 will fail to load.
123 | * To allow loading images of any size, pass BMPREAD_ANY_SIZE in flags.
124 | * Note that passing any of these flags may cause the output to be unusable as
125 | * an OpenGL texture, which may or may not matter to you.
126 | *
127 | * Most bitmap files can't include an alpha channel, so the default behavior is
128 | * to ignore any alpha values present in the file. Pass BMPREAD_ALPHA in flags
129 | * to capture alpha values from the file; in case of an absent alpha channel,
130 | * alpha values are output as 255 (this can be changed by redefining
131 | * BMPREAD_DEFAULT_ALPHA in bmpread.c). This allows fully loading 16- and
132 | * 32-bit bitmaps, which *can* include an alpha channel.
133 | */
134 | int bmpread(const char * bmp_file, unsigned int flags, bmpread_t * p_bmp_out);
135 |
136 |
137 | /* Frees memory allocated during bmpread(). Call bmpread_free() when you are
138 | * done using the bmpread_t struct (e.g. after you have passed the data on to
139 | * OpenGL).
140 | *
141 | * Inputs:
142 | * p_bmp - The pointer you previously passed to bmpread().
143 | *
144 | * Returns:
145 | * void
146 | */
147 | void bmpread_free(bmpread_t * p_bmp);
148 |
149 |
150 | #ifdef __cplusplus
151 | }
152 | #endif
153 |
154 | #endif
--------------------------------------------------------------------------------
/src/m/gl_draw.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by MoeMod on 2019-06-07.
3 | //
4 |
5 | #include "gl_draw.h"
6 |
7 | #include
8 |
9 | #include "StudioModel.h"
10 | #include "ViewerSettings.h"
11 |
12 | void setupRenderMode ();
13 |
14 | // Replaces gluPerspective. Sets the frustum to perspective mode.
15 | // fovY - Field of vision in degrees in the y direction
16 | // aspect - Aspect ratio of the viewport
17 | // near - The near clipping distance
18 | // far - The far clipping distance
19 | static void perspectiveGL (GLfloat fovY, GLfloat aspect, GLfloat _near, GLfloat _far)
20 | {
21 | GLfloat w, h;
22 |
23 | h = tan (fovY / 360 * Q_PI) * _near * (480.0f / 640.0f);
24 |
25 | w = h * aspect;
26 |
27 | glFrustum (-w, w, -h, h, _near, _far);
28 | }
29 |
30 | static void drawFloor ()
31 | {
32 | if (g_viewerSettings.use3dfx)
33 | {
34 | glBegin (GL_TRIANGLE_STRIP);
35 | glTexCoord2f (1.0f, 0.0f);
36 | glVertex3f (100.0f, 100.0f, 0.0f);
37 |
38 | glTexCoord2f (1.0f, 1.0f);
39 | glVertex3f (100.0f, -100.0f, 0.0f);
40 |
41 | glTexCoord2f (0.0f, 0.0f);
42 | glVertex3f (-100.0f, 100.0f, 0.0f);
43 |
44 | glTexCoord2f (0.0f, 1.0f);
45 | glVertex3f (-100.0f, -100.0f, 0.0f);
46 |
47 | glEnd ();
48 | }
49 | else
50 | {
51 | glBegin (GL_TRIANGLE_STRIP);
52 | glTexCoord2f (0.0f, 0.0f);
53 | glVertex3f (-100.0f, 100.0f, 0.0f);
54 |
55 | glTexCoord2f (0.0f, 1.0f);
56 | glVertex3f (-100.0f, -100.0f, 0.0f);
57 |
58 | glTexCoord2f (1.0f, 0.0f);
59 | glVertex3f (100.0f, 100.0f, 0.0f);
60 |
61 | glTexCoord2f (1.0f, 1.0f);
62 | glVertex3f (100.0f, -100.0f, 0.0f);
63 |
64 | glEnd ();
65 | }
66 | }
67 | void CHLMV_GL_Draw::Think()
68 | {
69 | g_studioModel.SetBlending (0, 0.0);
70 | g_studioModel.SetBlending (1, 0.0);
71 |
72 | auto CurrTime = std::chrono::high_resolution_clock::now();
73 |
74 | if (!m_bStopPlaying)
75 | g_studioModel.AdvanceFrame (std::chrono::duration_cast>(CurrTime - m_PrevTime).count() * g_viewerSettings.speedScale);
76 |
77 | m_PrevTime = CurrTime;
78 | }
79 |
80 | void CHLMV_GL_Draw::Draw()
81 | {
82 | glClearColor (g_viewerSettings.bgColor[0], g_viewerSettings.bgColor[1], g_viewerSettings.bgColor[2], 0.0f);
83 |
84 | if (g_viewerSettings.useStencil)
85 | glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
86 | else
87 | glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
88 |
89 | glViewport (0, 0, w2 (), h2 ());
90 |
91 | glEnable(GL_BLEND);
92 |
93 | //
94 | // show textures
95 | //
96 |
97 | if (g_viewerSettings.showTexture)
98 | {
99 | glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
100 | glMatrixMode (GL_PROJECTION);
101 | glLoadIdentity ();
102 |
103 | glOrtho (0.0f, (float) w2 (), (float) h2 (), 0.0f, 1.0f, -1.0f);
104 |
105 | studiohdr_t *hdr = g_studioModel.getTextureHeader ();
106 | if (hdr)
107 | {
108 | mstudiotexture_t *ptextures = (mstudiotexture_t *) ((byte *) hdr + hdr->textureindex);
109 | float w = (float) ptextures[g_viewerSettings.texture].width * g_viewerSettings.textureScale;
110 | float h = (float) ptextures[g_viewerSettings.texture].height * g_viewerSettings.textureScale;
111 |
112 | glMatrixMode (GL_MODELVIEW);
113 | glPushMatrix ();
114 | glLoadIdentity ();
115 |
116 | glDisable (GL_CULL_FACE);
117 | glDisable (GL_DEPTH_TEST);
118 |
119 | glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
120 | float x = ((float) w2 () - w) / 2;
121 | float y = ((float) h2 () - h) / 2;
122 |
123 | glDisable (GL_TEXTURE_2D);
124 | if (g_viewerSettings.showUVMap)
125 | {
126 | if (g_viewerSettings.showUVMapOverlay)
127 | {
128 | glColor4f (0.0f, 0.0f, 1.0f, 1.0f);
129 | glRectf (x, y, x + w, y + h);
130 | glEnable (GL_TEXTURE_2D);
131 | glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
132 | glBindTexture (GL_TEXTURE_2D, g_viewerSettings.texture + 3); //d_textureNames[0]);
133 |
134 | glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
135 | glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
136 | glBegin (GL_TRIANGLE_STRIP);
137 |
138 | glTexCoord2f (0, 0);
139 | glVertex2f (x, y);
140 |
141 | glTexCoord2f (1, 0);
142 | glVertex2f (x + w, y);
143 |
144 | glTexCoord2f (0, 1);
145 | glVertex2f (x, y + h);
146 |
147 | glTexCoord2f (1, 1);
148 | glVertex2f (x + w, y + h);
149 |
150 | glEnd ();
151 |
152 | }
153 | else
154 | {
155 | glColor4f (0.0f, 0.0f, 0.0f, 1.0f);
156 | glRectf (x, y, x + w, y + h);
157 | }
158 |
159 | hdr = g_studioModel.getStudioHeader ();
160 |
161 | if (hdr)
162 | {
163 | float s, t;
164 | int i, j, k, l;
165 | int index = -1;
166 | short *pskinref = (short *)((byte *)hdr + hdr->skinindex);
167 |
168 | for (k = 0; k < hdr->numbodyparts; k++)
169 | {
170 | mstudiobodyparts_t *pbodyparts = (mstudiobodyparts_t *) ((byte *) hdr + hdr->bodypartindex) + k;
171 |
172 | for (j = 0; j < pbodyparts->nummodels; j++)
173 | {
174 | mstudiomodel_t *pmodels = (mstudiomodel_t *) ((byte *) hdr + pbodyparts->modelindex) + j;
175 |
176 | for (l = 0; l < pmodels->nummesh; l++)
177 | {
178 | float s, t;
179 | short *ptricmds;
180 | int tri_type;
181 | mstudiomesh_t *pmesh = (mstudiomesh_t *) ((byte *) hdr + pmodels->meshindex) + l;
182 |
183 | ++index;
184 | if (pmesh->skinref != g_viewerSettings.texture
185 | || (g_viewerSettings.mesh != index && !g_viewerSettings.showAllMeshes))
186 | continue;
187 |
188 | ptricmds = (short *)((byte *)hdr + pmesh->triindex);
189 |
190 | s = 1.0/(float)ptextures[pskinref[pmesh->skinref]].width;
191 | t = 1.0/(float)ptextures[pskinref[pmesh->skinref]].height;
192 |
193 | glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
194 | glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
195 | glDisable (GL_TEXTURE_2D);
196 | glDisable (GL_CULL_FACE);
197 | glDisable (GL_DEPTH_TEST);
198 | if (g_viewerSettings.showSmoothLines)
199 | glEnable (GL_LINE_SMOOTH);
200 | else
201 | glDisable (GL_LINE_SMOOTH);
202 | glLineWidth (1.0);
203 |
204 | while ((i = *(ptricmds++)))
205 | {
206 | if (i < 0)
207 | {
208 | tri_type = GL_TRIANGLE_FAN;
209 | i = -i;
210 | }
211 | else
212 | {
213 | tri_type = GL_TRIANGLE_STRIP;
214 | }
215 |
216 | glBegin (tri_type);
217 | for ( ; i > 0; i--, ptricmds += 4)
218 | {
219 | glVertex3f (ptricmds[2] * s * w + x , ptricmds[3] * t * h + y , 1.0f);
220 | }
221 | glEnd ( );
222 | glDisable (GL_LINE_SMOOTH);
223 | }
224 | }
225 | }
226 | }
227 | }
228 | }
229 | else
230 | {
231 | glColor4f (0.0f, 0.0f, 0.0f, 0.0f);
232 | glRectf (x, y, x + w, y + h);
233 |
234 | glEnable (GL_TEXTURE_2D);
235 | glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
236 | glBindTexture (GL_TEXTURE_2D, g_viewerSettings.texture + 3); //d_textureNames[0]);
237 |
238 | glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
239 | glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
240 |
241 | glBegin (GL_TRIANGLE_STRIP);
242 |
243 | glTexCoord2f (0, 0);
244 | glVertex2f (x, y);
245 |
246 | glTexCoord2f (1, 0);
247 | glVertex2f (x + w, y);
248 |
249 | glTexCoord2f (0, 1);
250 | glVertex2f (x, y + h);
251 |
252 | glTexCoord2f (1, 1);
253 | glVertex2f (x + w, y + h);
254 |
255 | glEnd ();
256 | }
257 | glPopMatrix ();
258 |
259 | glClear (GL_DEPTH_BUFFER_BIT);
260 | glBindTexture (GL_TEXTURE_2D, 0);
261 | }
262 | return;
263 | }
264 |
265 | //
266 | // draw background
267 | //
268 |
269 | if (g_viewerSettings.showBackground && d_textureNames[0] && !g_viewerSettings.showTexture)
270 | {
271 | glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
272 | glMatrixMode (GL_PROJECTION);
273 | glLoadIdentity ();
274 |
275 | glOrtho (0.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f);
276 |
277 | glMatrixMode (GL_MODELVIEW);
278 | glPushMatrix ();
279 | glLoadIdentity ();
280 |
281 | glDisable (GL_CULL_FACE);
282 | glEnable (GL_TEXTURE_2D);
283 |
284 | glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
285 | glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
286 |
287 | glBindTexture (GL_TEXTURE_2D, d_textureNames[0]);
288 |
289 | glBegin (GL_TRIANGLE_STRIP);
290 |
291 | glTexCoord2f (0, 0);
292 | glVertex2f (0, 0);
293 |
294 | glTexCoord2f (1, 0);
295 | glVertex2f (1, 0);
296 |
297 | glTexCoord2f (0, 1);
298 | glVertex2f (0, 1);
299 |
300 | glTexCoord2f (1, 1);
301 | glVertex2f (1, 1);
302 |
303 | glEnd ();
304 |
305 | glPopMatrix ();
306 |
307 | glClear (GL_DEPTH_BUFFER_BIT);
308 | glBindTexture (GL_TEXTURE_2D, 0);
309 | }
310 |
311 | glMatrixMode (GL_PROJECTION);
312 | glLoadIdentity ();
313 | if(!g_viewerSettings.vieworiginmode)
314 | {
315 | perspectiveGL (g_viewerSettings.yaw, (GLfloat) w () / (GLfloat) h (), 1.0f, 4096.0f);
316 | }
317 | else
318 | {
319 | if(g_viewerSettings.viewaspect > 0.0f)
320 | perspectiveGL (g_viewerSettings.viewfov, 1 / g_viewerSettings.viewaspect, 1.0f, 4096.0f);
321 | else
322 | perspectiveGL (g_viewerSettings.viewfov, (GLfloat) w () / (GLfloat) h (), 1.0f, 4096.0f);
323 | }
324 |
325 | glMatrixMode (GL_MODELVIEW);
326 | glPushMatrix ();
327 | glLoadIdentity ();
328 |
329 | float trans[3] = {g_viewerSettings.trans[0], g_viewerSettings.trans[1], g_viewerSettings.trans[2]};
330 | float rot[3] = {g_viewerSettings.rot[0], g_viewerSettings.rot[1], g_viewerSettings.rot[2]};
331 |
332 | if(g_viewerSettings.vieworiginmode)
333 | {
334 | float dx = g_viewerSettings.vieworigintrans[0];
335 | float dy = g_viewerSettings.vieworigintrans[1];
336 | float dz = g_viewerSettings.vieworigintrans[2];
337 |
338 | trans[0] = -dx - 0.03f;
339 | trans[1] = dz + 1.0172f;
340 | trans[2] = dy - 0.02f;
341 | rot[0] = -90.0f;
342 | rot[1] = 90.0f;
343 | rot[2] = 0.0f;
344 | }
345 |
346 | glTranslatef(-trans[0], -trans[1], -trans[2]);
347 | glRotatef (rot[0], 1.0f, 0.0f, 0.0f);
348 | glRotatef (rot[1], 0.0f, 0.0f, 1.0f);
349 |
350 | // setup stencil buffer
351 | if (g_viewerSettings.useStencil)
352 | {
353 | /* Don't update color or depth. */
354 | glDisable(GL_DEPTH_TEST);
355 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
356 |
357 | /* Draw 1 into the stencil buffer. */
358 | glEnable(GL_STENCIL_TEST);
359 | glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
360 | glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
361 |
362 | /* Now render floor; floor pixels just get their stencil set to 1. */
363 | drawFloor();
364 |
365 | /* Re-enable update of color and depth. */
366 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
367 | glEnable(GL_DEPTH_TEST);
368 |
369 | /* Now, only render where stencil is set to 1. */
370 | glStencilFunc(GL_EQUAL, 1, 0xffffffff); /* draw if ==1 */
371 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
372 | }
373 |
374 | g_vright[0] = g_vright[1] = trans[2];
375 |
376 | if (g_viewerSettings.mirror)
377 | {
378 | glPushMatrix ();
379 | glScalef (1, 1, -1);
380 | glCullFace (GL_BACK);
381 | setupRenderMode ();
382 | g_studioModel.DrawModel ();
383 | glPopMatrix ();
384 | }
385 |
386 | if (g_viewerSettings.useStencil)
387 | glDisable (GL_STENCIL_TEST);
388 |
389 | setupRenderMode ();
390 |
391 | if (g_viewerSettings.vieworiginmode && g_viewerSettings.righthand)
392 | {
393 | glCullFace(GL_BACK);
394 | g_studioModel.DrawModel();
395 | }
396 | else
397 | {
398 | glCullFace(GL_FRONT);
399 | g_studioModel.DrawModel();
400 | }
401 |
402 | //
403 | // draw ground
404 | //
405 |
406 | if (g_viewerSettings.showGround)
407 | {
408 | glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
409 | glEnable (GL_DEPTH_TEST);
410 | glEnable (GL_CULL_FACE);
411 |
412 | if (g_viewerSettings.useStencil)
413 | glFrontFace (GL_CW);
414 | else
415 | glDisable (GL_CULL_FACE);
416 |
417 | glEnable (GL_BLEND);
418 | if (!d_textureNames[1])
419 | {
420 | glDisable (GL_TEXTURE_2D);
421 | glColor4f (g_viewerSettings.gColor[0], g_viewerSettings.gColor[1], g_viewerSettings.gColor[2], 0.7f);
422 | glBindTexture (GL_TEXTURE_2D, 0);
423 | }
424 | else
425 | {
426 | glEnable (GL_TEXTURE_2D);
427 | glColor4f (1.0f, 1.0f, 1.0f, 0.6f);
428 | glBindTexture (GL_TEXTURE_2D, d_textureNames[1]);
429 | }
430 |
431 | glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
432 |
433 | drawFloor ();
434 |
435 | glDisable (GL_BLEND);
436 |
437 | if (g_viewerSettings.useStencil)
438 | {
439 | glCullFace (GL_BACK);
440 | glColor4f (0.1f, 0.1f, 0.1f, 1.0f);
441 | glBindTexture (GL_TEXTURE_2D, 0);
442 | drawFloor ();
443 |
444 | glFrontFace (GL_CCW);
445 | }
446 | else
447 | glEnable (GL_CULL_FACE);
448 | }
449 |
450 | if (g_viewerSettings.showCrosshair)
451 | {
452 | glMatrixMode (GL_PROJECTION);
453 | glPushMatrix ();
454 | glLoadIdentity ();
455 | glOrtho (0.0f, w2 (), h2 (), 0.0, 0.0, 1.0);
456 | glMatrixMode (GL_MODELVIEW);
457 | glLoadIdentity ();
458 | glDisable (GL_DEPTH_TEST);
459 | glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
460 | glEnable (GL_BLEND);
461 | glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
462 | float w = (w2 () - 27.0f) * 0.5f;
463 | float h = (h2 () - 27.0f) * 0.5f;
464 | glDisable (GL_TEXTURE_2D);
465 | glColor4f (g_viewerSettings.guColor[0], g_viewerSettings.guColor[1], g_viewerSettings.guColor[2], 1.0f);
466 | float w2 = w + 10.0f;
467 | float w3 = w + 12.0f;
468 | float w4 = w + 13.0f;
469 | float w5 = w + 14.0f;
470 | float w6 = w + 15.0f;
471 | float w7 = w + 17.0f;
472 | float w8 = w + 27.0f;
473 |
474 | float h2 = h + 10.0f;
475 | float h3 = h + 12.0f;
476 | float h4 = h + 13.0f;
477 | float h5 = h + 14.0f;
478 | float h6 = h + 15.0f;
479 | float h7 = h + 17.0f;
480 | float h8 = h + 27.0f;
481 | glRectf (w4, h, w5, h2);
482 | glRectf (w4, h7, w5, h8);
483 | glRectf (w, h4, w2, h5);
484 | glRectf (w7, h4, w8, h5);
485 | glRectf (w4, h4, w5, h5);
486 | glColor4f (g_viewerSettings.guColor[0], g_viewerSettings.guColor[1], g_viewerSettings.guColor[2], 0.25f);
487 | glRectf (w3, h, w4, h2);
488 | glRectf (w5, h, w6, h2);
489 | glRectf (w3, h7, w4, h8);
490 | glRectf (w5, h7, w6, h8);
491 | glRectf (w, h3, w2, h4);
492 | glRectf (w, h5, w2, h6);
493 | glRectf (w7, h3, w8, h4);
494 | glRectf (w7, h5, w8, h6);
495 | glRectf (w4, h3, w5, h4);
496 | glRectf (w4, h5, w5, h6);
497 | glRectf (w3, h4, w4, h5);
498 | glRectf (w5, h4, w6, h5);
499 | glColor4f (g_viewerSettings.guColor[0], g_viewerSettings.guColor[1], g_viewerSettings.guColor[2], 0.125f);
500 | glRectf (w3, h3, w4, h4);
501 | glRectf (w5, h3, w6, h4);
502 | glRectf (w3, h5, w4, h6);
503 | glRectf (w5, h5, w6, h6);
504 | }
505 |
506 | if (g_viewerSettings.showGuideLines)
507 | {
508 | glMatrixMode (GL_PROJECTION);
509 | glPushMatrix ();
510 | glLoadIdentity ();
511 |
512 | glOrtho (0.0f, w2 (), h2 (), 0.0f, 0.0f, 1.0f);
513 |
514 | glMatrixMode (GL_MODELVIEW);
515 | glLoadIdentity ();
516 |
517 | glDisable (GL_DEPTH_TEST);
518 | glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
519 | glEnable (GL_BLEND);
520 |
521 | glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
522 | glDisable (GL_TEXTURE_2D);
523 |
524 | glColor4f (g_viewerSettings.guColor[0], g_viewerSettings.guColor[1], g_viewerSettings.guColor[2], 0.5f);
525 |
526 | glEnable (GL_LINE_SMOOTH);
527 | glEnable (GL_LINE_STIPPLE);
528 |
529 | glLineWidth (1.0f);
530 | glLineStipple (2, 0xFAFA);
531 |
532 | float w = w2 () - 0.5f;
533 | glBegin (GL_LINES);
534 | glVertex2f (w, h2 () + 14.0f);
535 | glVertex2f (w, h2 ());
536 | glEnd ();
537 |
538 | glDisable (GL_LINE_STIPPLE);
539 | glLineWidth (3.0f);
540 |
541 | glBegin (GL_LINES);
542 | w = w2 () * 0.085f;
543 | glVertex2f (w, 0.0f);
544 | glVertex2f (w, h2 ());
545 | w = w2 () * 0.915f;
546 | glVertex2f (w, 0.0f);
547 | glVertex2f (w, h2 ());
548 | glEnd ();
549 |
550 | glLineWidth (1.0f);
551 | glDisable (GL_LINE_SMOOTH);
552 | }
553 |
554 | glPopMatrix ();
555 |
556 | }
557 |
--------------------------------------------------------------------------------
/src/m/gl_draw.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by MoeMod on 2019-06-07.
3 | //
4 |
5 | #ifndef HLMV_GL_DRAW_H
6 | #define HLMV_GL_DRAW_H
7 |
8 | #include
9 |
10 |
11 | class CHLMV_GL_Draw
12 | {
13 | public:
14 | CHLMV_GL_Draw() : m_bStopPlaying(false) {}
15 |
16 | public:
17 | void Think();
18 | void Draw();
19 |
20 | public:
21 | void SetSize(int w, int h, int w2, int h2)
22 | {
23 | m_w = w;
24 | m_h = h;
25 | m_w2 = w2;
26 | m_h2 = h2;
27 | }
28 | void SetStopPlaying(bool stop)
29 | {
30 | m_bStopPlaying = stop;
31 | }
32 |
33 | public:
34 | int w() const { return m_w; }
35 | int h() const { return m_h; }
36 | int w2() const { return m_w2; }
37 | int h2() const { return m_h2; }
38 |
39 | private:
40 | int m_w;
41 | int m_h;
42 | int m_w2;
43 | int m_h2;
44 | typename std::chrono::high_resolution_clock::time_point m_PrevTime;
45 | int d_textureNames[2];
46 | bool m_bStopPlaying;
47 | };
48 |
49 |
50 | #endif //HLMV_GL_DRAW_H
51 |
--------------------------------------------------------------------------------
/src/m/mathlib.c:
--------------------------------------------------------------------------------
1 | /***
2 | *
3 | * Copyright (c) 1998, Valve LLC. All rights reserved.
4 | *
5 | * This product contains software technology licensed from Id
6 | * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
7 | * All Rights Reserved.
8 | *
9 | ****/
10 |
11 | // mathlib.c -- math primitives
12 |
13 | #pragma warning( disable : 4244 )
14 | #pragma warning( disable : 4237 )
15 | #pragma warning( disable : 4305 )
16 |
17 |
18 | /* #include "cmdlib.h" */
19 | #ifndef true
20 | #define true 1
21 | #endif /* true */
22 | #ifndef false
23 | #define false 0
24 | #endif /* false */
25 |
26 | #include "mathlib.h"
27 |
28 | vec3_t vec3_origin = {0,0,0};
29 |
30 |
31 | double VectorLength(vec3_t v)
32 | {
33 | int i;
34 | double length;
35 |
36 | length = 0;
37 | for (i=0 ; i< 3 ; i++)
38 | length += v[i]*v[i];
39 | length = sqrt (length); // FIXME
40 |
41 | return length;
42 | }
43 |
44 |
45 | int VectorCompare (vec3_t v1, vec3_t v2)
46 | {
47 | int i;
48 |
49 | for (i=0 ; i<3 ; i++)
50 | if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON)
51 | return false;
52 |
53 | return true;
54 | }
55 |
56 | vec_t Q_rint (vec_t in)
57 | {
58 | return floor (in + 0.5);
59 | }
60 |
61 | void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc)
62 | {
63 | vc[0] = va[0] + scale*vb[0];
64 | vc[1] = va[1] + scale*vb[1];
65 | vc[2] = va[2] + scale*vb[2];
66 | }
67 |
68 | void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
69 | {
70 | cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
71 | cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
72 | cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
73 | }
74 |
75 | vec_t _DotProduct (vec3_t v1, vec3_t v2)
76 | {
77 | return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
78 | }
79 |
80 | void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out)
81 | {
82 | out[0] = va[0]-vb[0];
83 | out[1] = va[1]-vb[1];
84 | out[2] = va[2]-vb[2];
85 | }
86 |
87 | void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out)
88 | {
89 | out[0] = va[0]+vb[0];
90 | out[1] = va[1]+vb[1];
91 | out[2] = va[2]+vb[2];
92 | }
93 |
94 | void _VectorCopy (vec3_t in, vec3_t out)
95 | {
96 | out[0] = in[0];
97 | out[1] = in[1];
98 | out[2] = in[2];
99 | }
100 |
101 | void _VectorScale (vec3_t v, vec_t scale, vec3_t out)
102 | {
103 | out[0] = v[0] * scale;
104 | out[1] = v[1] * scale;
105 | out[2] = v[2] * scale;
106 | }
107 |
108 | vec_t VectorNormalize (vec3_t v)
109 | {
110 | int i;
111 | double length;
112 |
113 | if ( fabs(v[1] - 0.000215956) < 0.0001)
114 | i=1;
115 |
116 | length = 0;
117 | for (i=0 ; i< 3 ; i++)
118 | length += v[i]*v[i];
119 | length = sqrt (length);
120 | if (length == 0)
121 | return 0;
122 |
123 | for (i=0 ; i< 3 ; i++)
124 | v[i] /= length;
125 |
126 | return length;
127 | }
128 |
129 | void VectorInverse (vec3_t v)
130 | {
131 | v[0] = -v[0];
132 | v[1] = -v[1];
133 | v[2] = -v[2];
134 | }
135 |
136 | void ClearBounds (vec3_t mins, vec3_t maxs)
137 | {
138 | mins[0] = mins[1] = mins[2] = 99999;
139 | maxs[0] = maxs[1] = maxs[2] = -99999;
140 | }
141 |
142 | void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs)
143 | {
144 | int i;
145 | vec_t val;
146 |
147 | for (i=0 ; i<3 ; i++)
148 | {
149 | val = v[i];
150 | if (val < mins[i])
151 | mins[i] = val;
152 | if (val > maxs[i])
153 | maxs[i] = val;
154 | }
155 | }
156 |
157 | void AngleMatrix (const vec3_t angles, float (*matrix)[4] )
158 | {
159 | float angle;
160 | float sr, sp, sy, cr, cp, cy;
161 |
162 | angle = angles[2] * (Q_PI*2 / 360);
163 | sy = sin(angle);
164 | cy = cos(angle);
165 | angle = angles[1] * (Q_PI*2 / 360);
166 | sp = sin(angle);
167 | cp = cos(angle);
168 | angle = angles[0] * (Q_PI*2 / 360);
169 | sr = sin(angle);
170 | cr = cos(angle);
171 |
172 | // matrix = (Z * Y) * X
173 | matrix[0][0] = cp*cy;
174 | matrix[1][0] = cp*sy;
175 | matrix[2][0] = -sp;
176 | matrix[0][1] = sr*sp*cy+cr*-sy;
177 | matrix[1][1] = sr*sp*sy+cr*cy;
178 | matrix[2][1] = sr*cp;
179 | matrix[0][2] = (cr*sp*cy+-sr*-sy);
180 | matrix[1][2] = (cr*sp*sy+-sr*cy);
181 | matrix[2][2] = cr*cp;
182 | matrix[0][3] = 0.0;
183 | matrix[1][3] = 0.0;
184 | matrix[2][3] = 0.0;
185 | }
186 |
187 | void AngleIMatrix (const vec3_t angles, float matrix[3][4] )
188 | {
189 | float angle;
190 | float sr, sp, sy, cr, cp, cy;
191 |
192 | angle = angles[2] * (Q_PI*2 / 360);
193 | sy = sin(angle);
194 | cy = cos(angle);
195 | angle = angles[1] * (Q_PI*2 / 360);
196 | sp = sin(angle);
197 | cp = cos(angle);
198 | angle = angles[0] * (Q_PI*2 / 360);
199 | sr = sin(angle);
200 | cr = cos(angle);
201 |
202 | // matrix = (Z * Y) * X
203 | matrix[0][0] = cp*cy;
204 | matrix[0][1] = cp*sy;
205 | matrix[0][2] = -sp;
206 | matrix[1][0] = sr*sp*cy+cr*-sy;
207 | matrix[1][1] = sr*sp*sy+cr*cy;
208 | matrix[1][2] = sr*cp;
209 | matrix[2][0] = (cr*sp*cy+-sr*-sy);
210 | matrix[2][1] = (cr*sp*sy+-sr*cy);
211 | matrix[2][2] = cr*cp;
212 | matrix[0][3] = 0.0;
213 | matrix[1][3] = 0.0;
214 | matrix[2][3] = 0.0;
215 | }
216 |
217 | void R_ConcatTransforms (const float in1[3][4], const float in2[3][4], float out[3][4])
218 | {
219 | out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
220 | in1[0][2] * in2[2][0];
221 | out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
222 | in1[0][2] * in2[2][1];
223 | out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
224 | in1[0][2] * in2[2][2];
225 | out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
226 | in1[0][2] * in2[2][3] + in1[0][3];
227 | out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
228 | in1[1][2] * in2[2][0];
229 | out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
230 | in1[1][2] * in2[2][1];
231 | out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
232 | in1[1][2] * in2[2][2];
233 | out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
234 | in1[1][2] * in2[2][3] + in1[1][3];
235 | out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
236 | in1[2][2] * in2[2][0];
237 | out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
238 | in1[2][2] * in2[2][1];
239 | out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
240 | in1[2][2] * in2[2][2];
241 | out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
242 | in1[2][2] * in2[2][3] + in1[2][3];
243 | }
244 |
245 |
246 |
247 | void VectorRotate (const vec3_t in1, const float in2[3][4], vec3_t out)
248 | {
249 | out[0] = DotProduct(in1, in2[0]);
250 | out[1] = DotProduct(in1, in2[1]);
251 | out[2] = DotProduct(in1, in2[2]);
252 | }
253 |
254 |
255 | // rotate by the inverse of the matrix
256 | void VectorIRotate (const vec3_t in1, const float in2[3][4], vec3_t out)
257 | {
258 | out[0] = in1[0]*in2[0][0] + in1[1]*in2[1][0] + in1[2]*in2[2][0];
259 | out[1] = in1[0]*in2[0][1] + in1[1]*in2[1][1] + in1[2]*in2[2][1];
260 | out[2] = in1[0]*in2[0][2] + in1[1]*in2[1][2] + in1[2]*in2[2][2];
261 | }
262 |
263 |
264 | void VectorTransform (const vec3_t in1, const float in2[3][4], vec3_t out)
265 | {
266 | out[0] = DotProduct(in1, in2[0]) + in2[0][3];
267 | out[1] = DotProduct(in1, in2[1]) + in2[1][3];
268 | out[2] = DotProduct(in1, in2[2]) + in2[2][3];
269 | }
270 |
271 |
272 |
273 | void AngleQuaternion( const vec3_t angles, vec4_t quaternion )
274 | {
275 | float angle;
276 | float sr, sp, sy, cr, cp, cy;
277 |
278 | // FIXME: rescale the inputs to 1/2 angle
279 | angle = angles[2] * 0.5;
280 | sy = sin(angle);
281 | cy = cos(angle);
282 | angle = angles[1] * 0.5;
283 | sp = sin(angle);
284 | cp = cos(angle);
285 | angle = angles[0] * 0.5;
286 | sr = sin(angle);
287 | cr = cos(angle);
288 |
289 | quaternion[0] = sr*cp*cy-cr*sp*sy; // X
290 | quaternion[1] = cr*sp*cy+sr*cp*sy; // Y
291 | quaternion[2] = cr*cp*sy-sr*sp*cy; // Z
292 | quaternion[3] = cr*cp*cy+sr*sp*sy; // W
293 | }
294 |
295 | void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] )
296 | {
297 |
298 | matrix[0][0] = 1.0 - 2.0 * quaternion[1] * quaternion[1] - 2.0 * quaternion[2] * quaternion[2];
299 | matrix[1][0] = 2.0 * quaternion[0] * quaternion[1] + 2.0 * quaternion[3] * quaternion[2];
300 | matrix[2][0] = 2.0 * quaternion[0] * quaternion[2] - 2.0 * quaternion[3] * quaternion[1];
301 |
302 | matrix[0][1] = 2.0 * quaternion[0] * quaternion[1] - 2.0 * quaternion[3] * quaternion[2];
303 | matrix[1][1] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[2] * quaternion[2];
304 | matrix[2][1] = 2.0 * quaternion[1] * quaternion[2] + 2.0 * quaternion[3] * quaternion[0];
305 |
306 | matrix[0][2] = 2.0 * quaternion[0] * quaternion[2] + 2.0 * quaternion[3] * quaternion[1];
307 | matrix[1][2] = 2.0 * quaternion[1] * quaternion[2] - 2.0 * quaternion[3] * quaternion[0];
308 | matrix[2][2] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[1] * quaternion[1];
309 | }
310 |
311 | void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt )
312 | {
313 | int i;
314 | float omega, cosom, sinom, sclp, sclq;
315 |
316 | // decide if one of the quaternions is backwards
317 | float a = 0;
318 | float b = 0;
319 | for (i = 0; i < 4; i++) {
320 | a += (p[i]-q[i])*(p[i]-q[i]);
321 | b += (p[i]+q[i])*(p[i]+q[i]);
322 | }
323 | if (a > b) {
324 | for (i = 0; i < 4; i++) {
325 | q[i] = -q[i];
326 | }
327 | }
328 |
329 | cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3];
330 |
331 | if ((1.0 + cosom) > 0.00000001) {
332 | if ((1.0 - cosom) > 0.00000001) {
333 | omega = acos( cosom );
334 | sinom = sin( omega );
335 | sclp = sin( (1.0 - t)*omega) / sinom;
336 | sclq = sin( t*omega ) / sinom;
337 | }
338 | else {
339 | sclp = 1.0 - t;
340 | sclq = t;
341 | }
342 | for (i = 0; i < 4; i++) {
343 | qt[i] = sclp * p[i] + sclq * q[i];
344 | }
345 | }
346 | else {
347 | qt[0] = -p[1];
348 | qt[1] = p[0];
349 | qt[2] = -p[3];
350 | qt[3] = p[2];
351 | sclp = sin( (1.0 - t) * 0.5 * Q_PI);
352 | sclq = sin( t * 0.5 * Q_PI);
353 | for (i = 0; i < 3; i++) {
354 | qt[i] = sclp * p[i] + sclq * qt[i];
355 | }
356 | }
357 | }
358 |
359 |
360 |
--------------------------------------------------------------------------------
/src/m/mathlib.h:
--------------------------------------------------------------------------------
1 | /***
2 | *
3 | * Copyright (c) 1998, Valve LLC. All rights reserved.
4 | *
5 | * This product contains software technology licensed from Id
6 | * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
7 | * All Rights Reserved.
8 | *
9 | ****/
10 |
11 | #ifndef __MATHLIB__
12 | #define __MATHLIB__
13 |
14 | // mathlib.h
15 |
16 | #include
17 |
18 | #ifdef __cplusplus
19 | extern "C" {
20 | #endif
21 |
22 | #ifdef DOUBLEVEC_T
23 | typedef double vec_t;
24 | #else
25 | typedef float vec_t;
26 | #endif
27 | typedef vec_t vec3_t[3]; // x,y,z
28 | typedef vec_t vec4_t[4]; // x,y,z,w
29 |
30 | #define SIDE_FRONT 0
31 | #define SIDE_ON 2
32 | #define SIDE_BACK 1
33 | #define SIDE_CROSS -2
34 |
35 | #define Q_PI 3.14159265358979323846
36 |
37 | extern vec3_t vec3_origin;
38 |
39 | // Use this definition globally
40 | #define ON_EPSILON 0.01
41 | #define EQUAL_EPSILON 0.001
42 |
43 | int VectorCompare (vec3_t v1, vec3_t v2);
44 |
45 | #define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
46 | #define VectorFill(a,b) { (a)[0]=(b); (a)[1]=(b); (a)[2]=(b);}
47 | #define VectorAvg(a) ( ( (a)[0] + (a)[1] + (a)[2] ) / 3 )
48 | #define VectorSubtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];}
49 | #define VectorAdd(a,b,c) {(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];(c)[2]=(a)[2]+(b)[2];}
50 | #define VectorCopy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];}
51 | #define VectorScale(a,b,c) {(c)[0]=(b)*(a)[0];(c)[1]=(b)*(a)[1];(c)[2]=(b)*(a)[2];}
52 |
53 | vec_t Q_rint (vec_t in);
54 | vec_t _DotProduct (vec3_t v1, vec3_t v2);
55 | void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out);
56 | void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out);
57 | void _VectorCopy (vec3_t in, vec3_t out);
58 | void _VectorScale (vec3_t v, vec_t scale, vec3_t out);
59 |
60 | double VectorLength(vec3_t v);
61 |
62 | void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc);
63 |
64 | void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
65 | vec_t VectorNormalize (vec3_t v);
66 | void VectorInverse (vec3_t v);
67 |
68 | void ClearBounds (vec3_t mins, vec3_t maxs);
69 | void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs);
70 |
71 | void AngleMatrix (const vec3_t angles, float matrix[3][4] );
72 | void AngleIMatrix (const vec3_t angles, float matrix[3][4] );
73 | void R_ConcatTransforms (const float in1[3][4], const float in2[3][4], float out[3][4]);
74 |
75 | void VectorIRotate (const vec3_t in1, const float in2[3][4], vec3_t out);
76 | void VectorRotate (const vec3_t in1, const float in2[3][4], vec3_t out);
77 |
78 | void VectorTransform (const vec3_t in1, const float in2[3][4], vec3_t out);
79 |
80 | void AngleQuaternion( const vec3_t angles, vec4_t quaternion );
81 | void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] );
82 | void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt );
83 |
84 |
85 | #ifdef __cplusplus
86 | }
87 | #endif
88 |
89 | #endif
90 |
--------------------------------------------------------------------------------
/src/m/mod_decryptor.cpp:
--------------------------------------------------------------------------------
1 | #include "mod_decryptor.h"
2 | #include "IceKey.hpp"
3 | #include "StudioModel.h"
4 |
5 | #include
6 |
7 | static const byte g_pDecryptorKey_20[32] =
8 | {
9 | 0x32, 0xA6, 0x21, 0xE0, 0xAB, 0x6B, 0xF4, 0x2C,
10 | 0x93, 0xC6, 0xF1, 0x96, 0xFB, 0x38, 0x75, 0x68,
11 | 0xBA, 0x70, 0x13, 0x86, 0xE0, 0xB3, 0x71, 0xF4,
12 | 0xE3, 0x9B, 0x07, 0x22, 0x0C, 0xFE, 0x88, 0x3A
13 | };
14 |
15 | static const byte g_pDecryptorKey_21[32] =
16 | {
17 | 0x22, 0x7A, 0x19, 0x6F, 0x7B, 0x86, 0x7D, 0xE0,
18 | 0x8C, 0xC6, 0xF1, 0x96, 0xFB, 0x38, 0x75, 0x68,
19 | 0x88, 0x7A, 0x78, 0x86, 0x78, 0x86, 0x67, 0x70,
20 | 0xD9, 0x91, 0x07, 0x3A, 0x14, 0x74, 0xFE, 0x22
21 | };
22 |
23 | CIceKey g_Decryptor(4);
24 |
25 | void DecryptChunk(byte *pData, size_t uDataSize)
26 | {
27 | if (!uDataSize)
28 | return;
29 |
30 | size_t uCount = (uDataSize + 7) >> 3;
31 |
32 | while (uCount)
33 | {
34 | g_Decryptor.Decrypt(pData, pData);
35 | pData += 8;
36 | uCount--;
37 | }
38 | }
39 |
40 | void DecryptData(byte *pData, size_t uDataSize)
41 | {
42 | if (!uDataSize)
43 | return;
44 |
45 | do
46 | {
47 | size_t uTempSize = uDataSize;
48 |
49 | if (uTempSize > 1024)
50 | uTempSize = 1024;
51 |
52 | if (uTempSize & 7)
53 | return;
54 |
55 | DecryptChunk(pData, uTempSize);
56 | pData += uTempSize;
57 | uDataSize -= uTempSize;
58 | } while (uDataSize);
59 | }
60 |
61 | bool Mod_IsCSOEncryptedModel(const studiohdr_t *studiohdr)
62 | {
63 | return studiohdr->version == 20 || studiohdr->version == 21;
64 | }
65 |
66 | void Mod_DecryptModel(const char *model_name, studiohdr_t *studiohdr)
67 | {
68 | byte *buffer = reinterpret_cast(studiohdr);
69 |
70 | if (!strncmp(model_name, "models/player", 13))
71 | {
72 | if (studiohdr->numhitboxes == 21)
73 | studiohdr->numhitboxes = 20;
74 | }
75 |
76 | if (studiohdr->version == 20 || studiohdr->version == 21)
77 | {
78 | if (studiohdr->version == 20)
79 | g_Decryptor.SetKey(g_pDecryptorKey_20);
80 | else if (studiohdr->version == 21)
81 | g_Decryptor.SetKey(g_pDecryptorKey_21);
82 |
83 | mstudiotexture_t *ptexture = (mstudiotexture_t *)(buffer + studiohdr->textureindex);
84 |
85 | for (int i = 0; i < studiohdr->numtextures; i++)
86 | DecryptData(buffer + ptexture[i].index, (ptexture[i].width * ptexture[i].height) + (256 * 3));
87 |
88 | mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)(buffer + studiohdr->bodypartindex);
89 |
90 | for (int i = 0; i < studiohdr->numbodyparts; i++)
91 | {
92 | mstudiomodel_t *pmodel = (mstudiomodel_t *)(buffer + pbodypart[i].modelindex);
93 |
94 | for (int j = 0; j < pbodypart[i].nummodels; j++)
95 | {
96 | if (pmodel[j].numverts > 0)
97 | DecryptData(buffer + pmodel[j].vertindex, pmodel[j].numverts * sizeof(vec3_t));
98 | }
99 | }
100 |
101 | studiohdr->version = 10;
102 | }
103 | }
--------------------------------------------------------------------------------
/src/m/mod_decryptor.h:
--------------------------------------------------------------------------------
1 | #ifndef MOD_DECRYPTOR_H
2 | #define MOD_DECRYPTOR_H
3 |
4 | #include
5 |
6 | bool Mod_IsCSOEncryptedModel(const studiohdr_t *studiohdr);
7 | void Mod_DecryptModel(const char *model_name, studiohdr_t *studiohdr);
8 |
9 |
10 | #endif
--------------------------------------------------------------------------------
/src/m/studio.h:
--------------------------------------------------------------------------------
1 | /***
2 | *
3 | * Copyright (c) 1998, Valve LLC. All rights reserved.
4 | *
5 | * This product contains software technology licensed from Id
6 | * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
7 | * All Rights Reserved.
8 | *
9 | ****/
10 |
11 |
12 |
13 |
14 | #ifndef _STUDIO_H_
15 | #define _STUDIO_H_
16 |
17 | /*
18 | ==============================================================================
19 |
20 | STUDIO MODELS
21 |
22 | Studio models are position independent, so the cache manager can move them.
23 | ==============================================================================
24 | */
25 |
26 |
27 | #define MAXSTUDIOTRIANGLES 20000 // TODO: tune this
28 | #define MAXSTUDIOVERTS 2048 // TODO: tune this
29 | #define MAXSTUDIOSEQUENCES 256 // total animation sequences
30 | #define MAXSTUDIOSKINS 100 // total textures
31 | #define MAXSTUDIOSRCBONES 512 // bones allowed at source movement
32 | #define MAXSTUDIOBONES 128 // total bones actually used
33 | #define MAXSTUDIOMODELS 32 // sub-models per model
34 | #define MAXSTUDIOBODYPARTS 32
35 | #define MAXSTUDIOGROUPS 4
36 | #define MAXSTUDIOANIMATIONS 512 // per sequence
37 | #define MAXSTUDIOMESHES 256
38 | #define MAXSTUDIOEVENTS 1024
39 | #define MAXSTUDIOPIVOTS 256
40 | #define MAXSTUDIOCONTROLLERS 8
41 |
42 | typedef struct
43 | {
44 | int id;
45 | int version;
46 |
47 | char name[64];
48 | int length;
49 |
50 | vec3_t eyeposition; // ideal eye position
51 | vec3_t min; // ideal movement hull size
52 | vec3_t max;
53 |
54 | vec3_t bbmin; // clipping bounding box
55 | vec3_t bbmax;
56 |
57 | int flags;
58 |
59 | int numbones; // bones
60 | int boneindex;
61 |
62 | int numbonecontrollers; // bone controllers
63 | int bonecontrollerindex;
64 |
65 | int numhitboxes; // complex bounding boxes
66 | int hitboxindex;
67 |
68 | int numseq; // animation sequences
69 | int seqindex;
70 |
71 | int numseqgroups; // demand loaded sequences
72 | int seqgroupindex;
73 |
74 | int numtextures; // raw textures
75 | int textureindex;
76 | int texturedataindex;
77 |
78 | int numskinref; // replaceable textures
79 | int numskinfamilies;
80 | int skinindex;
81 |
82 | int numbodyparts;
83 | int bodypartindex;
84 |
85 | int numattachments; // queryable attachable points
86 | int attachmentindex;
87 |
88 | int soundtable;
89 | int soundindex;
90 | int soundgroups;
91 | int soundgroupindex;
92 |
93 | int numtransitions; // animation node to animation node transition graph
94 | int transitionindex;
95 | } studiohdr_t;
96 |
97 | // header for demand loaded sequence group data
98 | typedef struct
99 | {
100 | int id;
101 | int version;
102 |
103 | char name[64];
104 | int length;
105 | } studioseqhdr_t;
106 |
107 | // bones
108 | typedef struct
109 | {
110 | char name[32]; // bone name for symbolic links
111 | int parent; // parent bone
112 | int flags; // ??
113 | int bonecontroller[6]; // bone controller index, -1 == none
114 | float value[6]; // default DoF values
115 | float scale[6]; // scale for delta DoF values
116 | } mstudiobone_t;
117 |
118 |
119 | // bone controllers
120 | typedef struct
121 | {
122 | int bone; // -1 == 0
123 | int type; // X, Y, Z, XR, YR, ZR, M
124 | float start;
125 | float end;
126 | int rest; // byte index value at rest
127 | int index; // 0-3 user set controller, 4 mouth
128 | } mstudiobonecontroller_t;
129 |
130 | // intersection boxes
131 | typedef struct
132 | {
133 | int bone;
134 | int group; // intersection group
135 | vec3_t bbmin; // bounding box
136 | vec3_t bbmax;
137 | } mstudiobbox_t;
138 |
139 | // demand loaded sequence groups
140 | typedef struct
141 | {
142 | char label[32]; // textual name
143 | char name[64]; // file name
144 | int cache; // cache index pointer
145 | int data; // hack for group 0
146 | } mstudioseqgroup_t;
147 |
148 | // sequence descriptions
149 | typedef struct
150 | {
151 | char label[32]; // sequence label
152 |
153 | float fps; // frames per second
154 | int flags; // looping/non-looping flags
155 |
156 | int activity;
157 | int actweight;
158 |
159 | int numevents;
160 | int eventindex;
161 |
162 | int numframes; // number of frames per sequence
163 |
164 | int numpivots; // number of foot pivots
165 | int pivotindex;
166 |
167 | int motiontype;
168 | int motionbone;
169 | vec3_t linearmovement;
170 | int automoveposindex;
171 | int automoveangleindex;
172 |
173 | vec3_t bbmin; // per sequence bounding box
174 | vec3_t bbmax;
175 |
176 | int numblends;
177 | int animindex; // mstudioanim_t pointer relative to start of sequence group data
178 | // [blend][bone][X, Y, Z, XR, YR, ZR]
179 |
180 | int blendtype[2]; // X, Y, Z, XR, YR, ZR
181 | float blendstart[2]; // starting value
182 | float blendend[2]; // ending value
183 | int blendparent;
184 |
185 | int seqgroup; // sequence group for demand loading
186 |
187 | int entrynode; // transition node at entry
188 | int exitnode; // transition node at exit
189 | int nodeflags; // transition rules
190 |
191 | int nextseq; // auto advancing sequences
192 | } mstudioseqdesc_t;
193 |
194 | // events
195 | typedef struct
196 | {
197 | int frame;
198 | int event;
199 | int type;
200 | char options[64];
201 | } mstudioevent_t;
202 |
203 |
204 | // pivots
205 | typedef struct
206 | {
207 | vec3_t org; // pivot point
208 | int start;
209 | int end;
210 | } mstudiopivot_t;
211 |
212 | // attachment
213 | typedef struct
214 | {
215 | char name[32];
216 | int type;
217 | int bone;
218 | vec3_t org; // attachment point
219 | vec3_t vectors[3];
220 | } mstudioattachment_t;
221 |
222 | typedef struct
223 | {
224 | unsigned short offset[6];
225 | } mstudioanim_t;
226 |
227 | // animation frames
228 | typedef union
229 | {
230 | struct {
231 | byte valid;
232 | byte total;
233 | } num;
234 | short value;
235 | } mstudioanimvalue_t;
236 |
237 |
238 |
239 | // body part index
240 | typedef struct
241 | {
242 | char name[64];
243 | int nummodels;
244 | int base;
245 | int modelindex; // index into models array
246 | } mstudiobodyparts_t;
247 |
248 |
249 |
250 | // skin info
251 | typedef struct
252 | {
253 | char name[64];
254 | int flags;
255 | int width;
256 | int height;
257 | int index;
258 | } mstudiotexture_t;
259 |
260 |
261 | // skin families
262 | // short index[skinfamilies][skinref]
263 |
264 | // studio models
265 | typedef struct
266 | {
267 | char name[64];
268 |
269 | int type;
270 |
271 | float boundingradius;
272 |
273 | int nummesh;
274 | int meshindex;
275 |
276 | int numverts; // number of unique vertices
277 | int vertinfoindex; // vertex bone info
278 | int vertindex; // vertex vec3_t
279 | int numnorms; // number of unique surface normals
280 | int norminfoindex; // normal bone info
281 | int normindex; // normal vec3_t
282 |
283 | int numgroups; // deformation groups
284 | int groupindex;
285 | } mstudiomodel_t;
286 |
287 |
288 | // vec3_t boundingbox[model][bone][2]; // complex intersection info
289 |
290 |
291 | // meshes
292 | typedef struct
293 | {
294 | int numtris;
295 | int triindex;
296 | int skinref;
297 | int numnorms; // per mesh normals
298 | int normindex; // normal vec3_t
299 | } mstudiomesh_t;
300 |
301 | // triangles
302 | #if 0
303 | typedef struct
304 | {
305 | short vertindex; // index into vertex array
306 | short normindex; // index into normal array
307 | short s,t; // s,t position on skin
308 | } mstudiotrivert_t;
309 | #endif
310 |
311 | // lighting options
312 | #define STUDIO_NF_FLATSHADE 0x0001
313 | #define STUDIO_NF_CHROME 0x0002
314 | #define STUDIO_NF_FULLBRIGHT 0x0004
315 | #define STUDIO_NF_ADDITIVE 0x0020
316 | #define STUDIO_NF_TRANSPARENT 0x0040
317 |
318 |
319 | // motion flags
320 | #define STUDIO_X 0x0001
321 | #define STUDIO_Y 0x0002
322 | #define STUDIO_Z 0x0004
323 | #define STUDIO_XR 0x0008
324 | #define STUDIO_YR 0x0010
325 | #define STUDIO_ZR 0x0020
326 | #define STUDIO_LX 0x0040
327 | #define STUDIO_LY 0x0080
328 | #define STUDIO_LZ 0x0100
329 | #define STUDIO_AX 0x0200
330 | #define STUDIO_AY 0x0400
331 | #define STUDIO_AZ 0x0800
332 | #define STUDIO_AXR 0x1000
333 | #define STUDIO_AYR 0x2000
334 | #define STUDIO_AZR 0x4000
335 | #define STUDIO_TYPES 0x7FFF
336 | #define STUDIO_RLOOP 0x8000 // controller that wraps shortest distance
337 |
338 | // sequence flags
339 | #define STUDIO_LOOPING 0x0001
340 |
341 | // bone flags
342 | #define STUDIO_HAS_NORMALS 0x0001
343 | #define STUDIO_HAS_VERTICES 0x0002
344 | #define STUDIO_HAS_BBOX 0x0004
345 | #define STUDIO_HAS_CHROME 0x0008 // if any of the textures have chrome on them
346 |
347 | #define RAD_TO_STUDIO (32768.0/M_PI)
348 | #define STUDIO_TO_RAD (M_PI/32768.0)
349 |
350 | #endif
351 |
--------------------------------------------------------------------------------
/src/m/studio_utils.cpp:
--------------------------------------------------------------------------------
1 | /***
2 | *
3 | * Copyright (c) 1998, Valve LLC. All rights reserved.
4 | *
5 | * This product contains software technology licensed from Id
6 | * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
7 | * All Rights Reserved.
8 | *
9 | ****/
10 | // updates:
11 | // 1-4-99 fixed file texture load and file read bug
12 |
13 | ////////////////////////////////////////////////////////////////////////
14 | #include
15 | #include
16 | #include
17 | #include
18 | //#include
19 | #include "StudioModel.h"
20 | #include "mod_decryptor.h"
21 | #include
22 | #include
23 | #include "TGAlib.h"
24 | #include "bmpread.h"
25 |
26 |
27 | #pragma warning( disable : 4244 ) // double to float
28 |
29 | //extern float g_bonetransform[MAXSTUDIOBONES][3][4];
30 |
31 |
32 | StudioModel g_studioModel;
33 |
34 | ////////////////////////////////////////////////////////////////////////
35 |
36 | static int g_texnum = 3;
37 |
38 | //Mugsy - upped the maximum texture size to 512. All changes are the replacement of '256'
39 | //with this define, MAX_TEXTURE_DIMS
40 | #define MAX_TEXTURE_DIMS 512
41 |
42 | void StudioModel::UploadTexture(const mstudiotexture_t *ptexture, const byte *data, const byte *pal, int name)
43 | {
44 | // unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight;
45 | int i, j;
46 | int row1[MAX_TEXTURE_DIMS], row2[MAX_TEXTURE_DIMS], col1[MAX_TEXTURE_DIMS], col2[MAX_TEXTURE_DIMS];
47 | const byte *pix1, *pix2, *pix3, *pix4;
48 | byte *tex, *out;
49 |
50 | // convert texture to power of 2
51 | int outwidth;
52 | for (outwidth = 1; outwidth < ptexture->width; outwidth <<= 1)
53 | ;
54 |
55 | if (outwidth > MAX_TEXTURE_DIMS)
56 | outwidth = MAX_TEXTURE_DIMS;
57 |
58 | int outheight;
59 | for (outheight = 1; outheight < ptexture->height; outheight <<= 1)
60 | ;
61 |
62 | if (outheight > MAX_TEXTURE_DIMS)
63 | outheight = MAX_TEXTURE_DIMS;
64 |
65 | tex = out = (byte *)malloc( outwidth * outheight * 4);
66 | if (!out)
67 | {
68 | return;
69 | }
70 | /*
71 | int k = 0;
72 | for (i = 0; i < ptexture->height; i++)
73 | {
74 | for (j = 0; j < ptexture->width; j++)
75 | {
76 |
77 | in[k++] = pal[data[i * ptexture->width + j] * 3 + 0];
78 | in[k++] = pal[data[i * ptexture->width + j] * 3 + 1];
79 | in[k++] = pal[data[i * ptexture->width + j] * 3 + 2];
80 | in[k++] = 0xff;;
81 | }
82 | }
83 |
84 | gluScaleImage (GL_RGBA, ptexture->width, ptexture->height, GL_UNSIGNED_BYTE, in, outwidth, outheight, GL_UNSIGNED_BYTE, out);
85 | free (in);
86 | */
87 |
88 | for (i = 0; i < outwidth; i++)
89 | {
90 | col1[i] = (int) ((i + 0.25) * (ptexture->width / (float)outwidth));
91 | col2[i] = (int) ((i + 0.75) * (ptexture->width / (float)outwidth));
92 | }
93 |
94 | for (i = 0; i < outheight; i++)
95 | {
96 | row1[i] = (int) ((i + 0.25) * (ptexture->height / (float)outheight)) * ptexture->width;
97 | row2[i] = (int) ((i + 0.75) * (ptexture->height / (float)outheight)) * ptexture->width;
98 | }
99 |
100 | // scale down and convert to 32bit RGB
101 | for (i=0 ; i>2;
111 | out[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
112 | out[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
113 | out[3] = 0xFF;
114 | }
115 | }
116 |
117 | glBindTexture( GL_TEXTURE_2D, name ); //g_texnum );
118 | glTexImage2D( GL_TEXTURE_2D, 0, 3/*??*/, outwidth, outheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex );
119 | glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
120 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
121 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
122 |
123 | // ptexture->width = outwidth;
124 | // ptexture->height = outheight;
125 | //ptexture->index = name; //g_texnum;
126 |
127 | free( tex );
128 | }
129 |
130 |
131 |
132 | void
133 | StudioModel::FreeModel ()
134 | {
135 | if (m_pstudiohdr)
136 | free (m_pstudiohdr);
137 |
138 | if (m_ptexturehdr && m_owntexmodel)
139 | free (m_ptexturehdr);
140 |
141 | m_pstudiohdr = m_ptexturehdr = 0;
142 | m_owntexmodel = false;
143 |
144 | int i;
145 | for (i = 0; i < 32; i++)
146 | {
147 | if (m_panimhdr[i])
148 | {
149 | free (m_panimhdr[i]);
150 | m_panimhdr[i] = 0;
151 | }
152 | }
153 |
154 | // deleting textures
155 | g_texnum -= 3;
156 | int textures[MAXSTUDIOSKINS];
157 | for (i = 0; i < g_texnum; i++)
158 | textures[i] = i + 3;
159 |
160 | glDeleteTextures (g_texnum, (const GLuint *) textures);
161 |
162 | g_texnum = 3;
163 | }
164 |
165 |
166 |
167 | studiohdr_t *StudioModel::LoadModel( const char *modelname )
168 | {
169 | FILE *fp;
170 | long size;
171 | void *buffer;
172 |
173 | if (!modelname)
174 | return 0;
175 |
176 | // load the model
177 | if( (fp = fopen( modelname, "rb" )) == NULL)
178 | return 0;
179 |
180 | fseek( fp, 0, SEEK_END );
181 | size = ftell( fp );
182 | fseek( fp, 0, SEEK_SET );
183 |
184 | buffer = malloc( size );
185 | if (!buffer)
186 | {
187 | fclose (fp);
188 | return 0;
189 | }
190 |
191 | fread( buffer, size, 1, fp );
192 | fclose( fp );
193 |
194 |
195 | if (strncmp ((const char *) buffer, "IDST", 4) &&
196 | strncmp ((const char *) buffer, "IDSQ", 4))
197 | {
198 | free (buffer);
199 | return nullptr;
200 | }
201 |
202 | if (!strncmp ((const char *) buffer, "IDSQ", 4) && !m_pstudiohdr)
203 | {
204 | free (buffer);
205 | return nullptr;
206 | }
207 |
208 | return (studiohdr_t *)buffer;
209 | }
210 |
211 | void StudioModel::LoadModelTextures( const studiohdr_t *phdr)
212 | {
213 | const byte *pin = reinterpret_cast(phdr);
214 | const mstudiotexture_t *ptexture = reinterpret_cast(pin + phdr->textureindex);
215 |
216 | if (phdr->textureindex > 0 && phdr->numtextures <= MAXSTUDIOSKINS)
217 | {
218 | int n = phdr->numtextures;
219 | for (int i = 0; i < n; i++)
220 | {
221 | // strcpy( name, mod->name );
222 | // strcpy( name, ptexture[i].name );
223 | UploadTexture( &ptexture[i], pin + ptexture[i].index, pin + ptexture[i].width * ptexture[i].height + ptexture[i].index, g_texnum++ );
224 | }
225 | }
226 |
227 | // UNDONE: free texture memory
228 | }
229 |
230 | void StudioModel::LoadModelTexturesCSO( studiohdr_t *phdr, const char *texturePath)
231 | {
232 | byte *pin = reinterpret_cast(phdr);
233 | mstudiotexture_t *ptexture = reinterpret_cast(pin + phdr->textureindex);
234 |
235 | if (phdr->textureindex > 0 && phdr->numtextures <= MAXSTUDIOSKINS)
236 | {
237 | int n = phdr->numtextures;
238 | for (int i = 0; i < n; i++)
239 | {
240 | // strcpy( name, mod->name );
241 | // strcpy( name, ptexture[i].name );
242 | if(isCSOExternalTexture(ptexture[i]))
243 | {
244 | std::string path = texturePath;
245 | path.push_back('/');
246 | path += (ptexture[i].name);
247 |
248 | if(UploadTextureTGA(&ptexture[i], path.c_str(), g_texnum) || UploadTextureBMP(&ptexture[i], path.c_str(), g_texnum))
249 | {
250 | // ...
251 | char buffer[64] = "*CSO* ";
252 | strcpy(ptexture[i].name, strcat(buffer, ptexture[i].name));
253 | }
254 | else
255 | {
256 | UploadTexture( &ptexture[i], pin + ptexture[i].index, pin + ptexture[i].width * ptexture[i].height + ptexture[i].index, g_texnum );
257 | }
258 | ++g_texnum;
259 | }
260 | else
261 | {
262 | UploadTexture( &ptexture[i], pin + ptexture[i].index, pin + ptexture[i].width * ptexture[i].height + ptexture[i].index, g_texnum++ );
263 | }
264 |
265 | }
266 | }
267 |
268 | // UNDONE: free texture memory
269 | }
270 |
271 | bool StudioModel::PostLoadModel( studiohdr_t *phdr, const char *modelname )
272 | {
273 | studiohdr_t *ptexturehdr = nullptr;
274 | bool owntexmodel = false;
275 |
276 | // preload textures
277 | if (phdr->numtextures == 0)
278 | {
279 | char texturename[256];
280 |
281 | strcpy( texturename, modelname );
282 | strcpy( &texturename[strlen(texturename) - 4], "T.mdl" );
283 |
284 | ptexturehdr = LoadModel( texturename );
285 | if (!ptexturehdr)
286 | {
287 | FreeModel ();
288 | return false;
289 | }
290 | owntexmodel = true;
291 | }
292 | else
293 | {
294 | ptexturehdr = phdr;
295 | owntexmodel = false;
296 | }
297 |
298 | // preload animations
299 | if (phdr->numseqgroups > 1)
300 | {
301 | for (int i = 1; i < phdr->numseqgroups; i++)
302 | {
303 | char seqgroupname[256];
304 |
305 | strcpy( seqgroupname, modelname );
306 | sprintf( &seqgroupname[strlen(seqgroupname) - 4], "%02d.mdl", i );
307 |
308 | m_panimhdr[i] = LoadModel( seqgroupname );
309 | if (!m_panimhdr[i])
310 | {
311 | FreeModel ();
312 | return false;
313 | }
314 | }
315 | }
316 |
317 | m_pstudiohdr = phdr;
318 | m_ptexturehdr = ptexturehdr;
319 | m_owntexmodel = owntexmodel;
320 |
321 | SetSequence (0);
322 | SetController (0, 0.0f);
323 | SetController (1, 0.0f);
324 | SetController (2, 0.0f);
325 | SetController (3, 0.0f);
326 | SetMouth (0.0f);
327 |
328 | int n;
329 | for (n = 0; n < phdr->numbodyparts; n++)
330 | SetBodygroup (n, 0);
331 |
332 | SetSkin (0);
333 | /*
334 | vec3_t mins, maxs;
335 | ExtractBbox (mins, maxs);
336 | if (mins[2] < 5.0f)
337 | m_origin[2] = -mins[2];
338 | */
339 |
340 | return true;
341 | }
342 |
343 |
344 |
345 | bool StudioModel::SaveModel ( const char *modelname )
346 | {
347 | if (!modelname)
348 | return false;
349 |
350 | if (!m_pstudiohdr)
351 | return false;
352 |
353 | FILE *file;
354 |
355 | file = fopen (modelname, "wb");
356 | if (!file)
357 | return false;
358 |
359 | std::vector data_to_save(reinterpret_cast(m_pstudiohdr), reinterpret_cast(m_pstudiohdr) + m_pstudiohdr->length);
360 |
361 | {
362 | studiohdr_t *phdr = reinterpret_cast(data_to_save.data());
363 | mstudiotexture_t *ptexture = reinterpret_cast(data_to_save.data() + phdr->textureindex);
364 |
365 | if (phdr->textureindex > 0 && phdr->numtextures <= MAXSTUDIOSKINS) {
366 | int n = phdr->numtextures;
367 | for (int i = 0; i < n; i++) {
368 | if(strncmp(ptexture[i].name, "*CSO* ", 5) != 0)
369 | continue;
370 | // TODO : convert and save external texture
371 | #if 0
372 | auto old_size = data_to_save.size();
373 | {
374 | data_to_save.resize(old_size + ptexture[i].width * ptexture[i].height * 3 + 256);
375 | // due to vector resize, pointers may be expired
376 | phdr = reinterpret_cast(data_to_save.data());
377 | ptexture = reinterpret_cast(data_to_save.data() + phdr->textureindex);
378 | }
379 |
380 | //glGetTexImage(GL_TEXTURE_2D, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, data_to_save.data() + old_size);
381 | ptexture[i].index = old_size;
382 | #endif
383 |
384 | {
385 | char szName[64];
386 | strcpy(szName, ptexture[i].name + 5);
387 | strcpy(ptexture[i].name, szName);
388 | ptexture[i].width = 4;
389 | ptexture[i].height = 1;
390 | }
391 | }
392 | }
393 | }
394 |
395 | fwrite (data_to_save.data(), sizeof (byte), data_to_save.size(), file);
396 | fclose (file);
397 |
398 | // write texture model
399 | if (m_owntexmodel && m_ptexturehdr)
400 | {
401 | char texturename[256];
402 |
403 | strcpy( texturename, modelname );
404 | strcpy( &texturename[strlen(texturename) - 4], "T.mdl" );
405 |
406 | file = fopen (texturename, "wb");
407 | if (file)
408 | {
409 | fwrite (m_ptexturehdr, sizeof (byte), m_ptexturehdr->length, file);
410 | fclose (file);
411 | }
412 | }
413 |
414 | // write seq groups
415 | if (m_pstudiohdr->numseqgroups > 1)
416 | {
417 | for (int i = 1; i < m_pstudiohdr->numseqgroups; i++)
418 | {
419 | char seqgroupname[256];
420 |
421 | strcpy( seqgroupname, modelname );
422 | sprintf( &seqgroupname[strlen(seqgroupname) - 4], "%02d.mdl", i );
423 |
424 | file = fopen (seqgroupname, "wb");
425 | if (file)
426 | {
427 | fwrite (m_panimhdr[i], sizeof (byte), m_panimhdr[i]->length, file);
428 | fclose (file);
429 | }
430 | }
431 | }
432 |
433 | return true;
434 | }
435 |
436 |
437 |
438 | ////////////////////////////////////////////////////////////////////////
439 |
440 | int StudioModel::GetSequence( )
441 | {
442 | return m_sequence;
443 | }
444 |
445 | int StudioModel::SetSequence( int iSequence )
446 | {
447 | if (iSequence > m_pstudiohdr->numseq)
448 | return m_sequence;
449 |
450 | m_sequence = iSequence;
451 | m_frame = 0;
452 |
453 | return m_sequence;
454 | }
455 |
456 |
457 | void StudioModel::ExtractBbox( float *mins, float *maxs )
458 | {
459 | mstudioseqdesc_t *pseqdesc;
460 |
461 | pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex);
462 |
463 | mins[0] = pseqdesc[ m_sequence ].bbmin[0];
464 | mins[1] = pseqdesc[ m_sequence ].bbmin[1];
465 | mins[2] = pseqdesc[ m_sequence ].bbmin[2];
466 |
467 | maxs[0] = pseqdesc[ m_sequence ].bbmax[0];
468 | maxs[1] = pseqdesc[ m_sequence ].bbmax[1];
469 | maxs[2] = pseqdesc[ m_sequence ].bbmax[2];
470 | }
471 |
472 |
473 |
474 | void StudioModel::GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed )
475 | {
476 | mstudioseqdesc_t *pseqdesc;
477 |
478 | pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex) + (int)m_sequence;
479 |
480 | if (pseqdesc->numframes > 1)
481 | {
482 | *pflFrameRate = 256 * pseqdesc->fps / (pseqdesc->numframes - 1);
483 | *pflGroundSpeed = sqrt( pseqdesc->linearmovement[0]*pseqdesc->linearmovement[0]+ pseqdesc->linearmovement[1]*pseqdesc->linearmovement[1]+ pseqdesc->linearmovement[2]*pseqdesc->linearmovement[2] );
484 | *pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1);
485 | }
486 | else
487 | {
488 | *pflFrameRate = 256.0;
489 | *pflGroundSpeed = 0.0;
490 | }
491 | }
492 |
493 |
494 |
495 | float StudioModel::SetController( int iController, float flValue )
496 | {
497 | if (!m_pstudiohdr)
498 | return 0.0f;
499 |
500 | mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bonecontrollerindex);
501 |
502 | // find first controller that matches the index
503 | int i;
504 | for (i = 0; i < m_pstudiohdr->numbonecontrollers; i++, pbonecontroller++)
505 | {
506 | if (pbonecontroller->index == iController)
507 | break;
508 | }
509 | if (i >= m_pstudiohdr->numbonecontrollers)
510 | return flValue;
511 |
512 | // wrap 0..360 if it's a rotational controller
513 | if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
514 | {
515 | // ugly hack, invert value if end < start
516 | if (pbonecontroller->end < pbonecontroller->start)
517 | flValue = -flValue;
518 |
519 | // does the controller not wrap?
520 | if (pbonecontroller->start + 359.0 >= pbonecontroller->end)
521 | {
522 | if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180)
523 | flValue = flValue - 360;
524 | if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180)
525 | flValue = flValue + 360;
526 | }
527 | else
528 | {
529 | if (flValue > 360)
530 | flValue = flValue - (int)(flValue / 360.0) * 360.0;
531 | else if (flValue < 0)
532 | flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0;
533 | }
534 | }
535 |
536 | int setting = (int) (255 * (flValue - pbonecontroller->start) /
537 | (pbonecontroller->end - pbonecontroller->start));
538 |
539 | if (setting < 0) setting = 0;
540 | if (setting > 255) setting = 255;
541 | m_controller[iController] = setting;
542 |
543 | return setting * (1.0 / 255.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start;
544 | }
545 |
546 |
547 | float StudioModel::SetMouth( float flValue )
548 | {
549 | if (!m_pstudiohdr)
550 | return 0.0f;
551 |
552 | mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bonecontrollerindex);
553 |
554 | // find first controller that matches the mouth
555 | for (int i = 0; i < m_pstudiohdr->numbonecontrollers; i++, pbonecontroller++)
556 | {
557 | if (pbonecontroller->index == 4)
558 | break;
559 | }
560 |
561 | // wrap 0..360 if it's a rotational controller
562 | if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
563 | {
564 | // ugly hack, invert value if end < start
565 | if (pbonecontroller->end < pbonecontroller->start)
566 | flValue = -flValue;
567 |
568 | // does the controller not wrap?
569 | if (pbonecontroller->start + 359.0 >= pbonecontroller->end)
570 | {
571 | if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180)
572 | flValue = flValue - 360;
573 | if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180)
574 | flValue = flValue + 360;
575 | }
576 | else
577 | {
578 | if (flValue > 360)
579 | flValue = flValue - (int)(flValue / 360.0) * 360.0;
580 | else if (flValue < 0)
581 | flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0;
582 | }
583 | }
584 |
585 | int setting = (int) (64 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start));
586 |
587 | if (setting < 0) setting = 0;
588 | if (setting > 64) setting = 64;
589 | m_mouth = setting;
590 |
591 | return setting * (1.0 / 64.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start;
592 | }
593 |
594 |
595 | float StudioModel::SetBlending( int iBlender, float flValue )
596 | {
597 | mstudioseqdesc_t *pseqdesc;
598 |
599 | if (!m_pstudiohdr)
600 | return 0.0f;
601 |
602 | pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex) + (int)m_sequence;
603 |
604 | if (pseqdesc->blendtype[iBlender] == 0)
605 | return flValue;
606 |
607 | if (pseqdesc->blendtype[iBlender] & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
608 | {
609 | // ugly hack, invert value if end < start
610 | if (pseqdesc->blendend[iBlender] < pseqdesc->blendstart[iBlender])
611 | flValue = -flValue;
612 |
613 | // does the controller not wrap?
614 | if (pseqdesc->blendstart[iBlender] + 359.0 >= pseqdesc->blendend[iBlender])
615 | {
616 | if (flValue > ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) + 180)
617 | flValue = flValue - 360;
618 | if (flValue < ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) - 180)
619 | flValue = flValue + 360;
620 | }
621 | }
622 |
623 | int setting = (int) (255 * (flValue - pseqdesc->blendstart[iBlender]) / (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]));
624 |
625 | if (setting < 0) setting = 0;
626 | if (setting > 255) setting = 255;
627 |
628 | m_blending[iBlender] = setting;
629 |
630 | return setting * (1.0 / 255.0) * (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]) + pseqdesc->blendstart[iBlender];
631 | }
632 |
633 |
634 |
635 | int StudioModel::SetBodygroup( int iGroup, int iValue )
636 | {
637 | if (!m_pstudiohdr)
638 | return 0;
639 |
640 | if (iGroup > m_pstudiohdr->numbodyparts)
641 | return -1;
642 |
643 | mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bodypartindex) + iGroup;
644 |
645 | int iCurrent = (m_bodynum / pbodypart->base) % pbodypart->nummodels;
646 |
647 | if (iValue >= pbodypart->nummodels)
648 | return iCurrent;
649 |
650 | m_bodynum = (m_bodynum - (iCurrent * pbodypart->base) + (iValue * pbodypart->base));
651 |
652 | return iValue;
653 | }
654 |
655 |
656 | int StudioModel::SetSkin( int iValue )
657 | {
658 | if (!m_ptexturehdr)
659 | return 0;
660 |
661 | if (iValue >= m_ptexturehdr->numskinfamilies)
662 | {
663 | return m_skinnum;
664 | }
665 |
666 | m_skinnum = iValue;
667 |
668 | return iValue;
669 | }
670 |
671 |
672 |
673 | void StudioModel::scaleMeshes (float scale)
674 | {
675 | if (!m_pstudiohdr)
676 | return;
677 |
678 | int i, j, k;
679 |
680 | // scale verts
681 | int tmp = m_bodynum;
682 | for (i = 0; i < m_pstudiohdr->numbodyparts; i++)
683 | {
684 | mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bodypartindex) + i;
685 | for (j = 0; j < pbodypart->nummodels; j++)
686 | {
687 | SetBodygroup (i, j);
688 | SetupModel (i);
689 |
690 | vec3_t *pstudioverts = (vec3_t *)((byte *)m_pstudiohdr + m_pmodel->vertindex);
691 |
692 | for (k = 0; k < m_pmodel->numverts; k++)
693 | VectorScale (pstudioverts[k], scale, pstudioverts[k]);
694 | }
695 | }
696 |
697 | m_bodynum = tmp;
698 |
699 | // scale complex hitboxes
700 | mstudiobbox_t *pbboxes = (mstudiobbox_t *) ((byte *) m_pstudiohdr + m_pstudiohdr->hitboxindex);
701 | for (i = 0; i < m_pstudiohdr->numhitboxes; i++)
702 | {
703 | VectorScale (pbboxes[i].bbmin, scale, pbboxes[i].bbmin);
704 | VectorScale (pbboxes[i].bbmax, scale, pbboxes[i].bbmax);
705 | }
706 |
707 | // scale bounding boxes
708 | mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex);
709 | for (i = 0; i < m_pstudiohdr->numseq; i++)
710 | {
711 | VectorScale (pseqdesc[i].bbmin, scale, pseqdesc[i].bbmin);
712 | VectorScale (pseqdesc[i].bbmax, scale, pseqdesc[i].bbmax);
713 | }
714 |
715 | // maybe scale exeposition, pivots, attachments
716 | }
717 |
718 |
719 |
720 | void StudioModel::scaleBones (float scale)
721 | {
722 | if (!m_pstudiohdr)
723 | return;
724 |
725 | mstudiobone_t *pbones = (mstudiobone_t *) ((byte *) m_pstudiohdr + m_pstudiohdr->boneindex);
726 | for (int i = 0; i < m_pstudiohdr->numbones; i++)
727 | {
728 | for (int j = 0; j < 3; j++)
729 | {
730 | pbones[i].value[j] *= scale;
731 | pbones[i].scale[j] *= scale;
732 | }
733 | }
734 | }
735 |
736 | bool StudioModel::hasCSOTexture(const studiohdr_t *phdr)
737 | {
738 | const byte *pin = reinterpret_cast(phdr);
739 | const mstudiotexture_t *ptextures = reinterpret_cast(pin + phdr->textureindex);
740 |
741 | if (phdr->textureindex > 0 && phdr->numtextures <= MAXSTUDIOSKINS)
742 | {
743 | int n = phdr->numtextures;
744 | return std::any_of(ptextures, ptextures + n, isCSOExternalTexture);
745 | }
746 |
747 | return false;
748 | }
749 | bool StudioModel::isCSOExternalTexture(const mstudiotexture_t &texture)
750 | {
751 | return texture.width == 4 && texture.height == 1 && (texture.name[0] == '#' || texture.name[0] == '$');
752 | }
753 |
754 | bool StudioModel::UploadTextureTGA(mstudiotexture_t *ptexture, const char *path, int name)
755 | {
756 | tImageTGA *tga = tgaLoad(path);
757 | if(tga->status != TGA_OK)
758 | return false;
759 | std::unique_ptr ptga(tga, tgaDestroy);
760 |
761 | glBindTexture( GL_TEXTURE_2D, name ); //g_texnum );
762 | if(tga->pixelDepth == 32)
763 | glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, tga->width, tga->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tga->imageData );
764 | else
765 | glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB5_A1, tga->width, tga->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tga->imageData );
766 | glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
767 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
768 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
769 |
770 | ptexture->width = tga->width;
771 | ptexture->height = tga->height;
772 |
773 | return true;
774 | }
775 |
776 | bool StudioModel::UploadTextureBMP(mstudiotexture_t *ptexture, const char *path, int name)
777 | {
778 | bmpread_t bitmap;
779 | if(!bmpread(path, BMPREAD_TOP_DOWN, &bitmap))
780 | {
781 | return false;
782 | }
783 | std::unique_ptr pbmp(&bitmap, bmpread_free);
784 |
785 | /* At this point, bitmap.width and .height hold the pixel dimensions of the
786 | * file, and bitmap.data holds the raw pixel data in RGB triplets.
787 | */
788 |
789 | glBindTexture(GL_TEXTURE_2D, name);
790 |
791 | glTexImage2D(GL_TEXTURE_2D, 0, 3, bitmap.width, bitmap.height, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmap.data);
792 |
793 | glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
794 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
795 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
796 |
797 | ptexture->width = bitmap.width;
798 | ptexture->height = bitmap.height;
799 |
800 | return true;
801 | }
802 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv_application.h"
2 |
3 | int main(int argc, char *argv[])
4 | {
5 | return HLMVApplication(argc, argv).exec();
6 | }
7 |
--------------------------------------------------------------------------------
/src/v/hlmv.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | QtGuiApplication1::QtGuiApplication1(QWidget *parent)
8 | : QMainWindow(parent)
9 | {
10 | this->setMinimumWidth(QApplication::primaryScreen()->availableVirtualSize().width() / 2);
11 | this->setMinimumHeight(width() / 3 * 2);
12 |
13 | ui.setupUi(this);
14 |
15 | connect(ui.actionOpen, &QAction::triggered, this, &QtGuiApplication1::OnActionOpen);
16 | connect(ui.actionSave, &QAction::triggered, this, &QtGuiApplication1::OnActionSave);
17 | connect(ui.actionClear, &QAction::triggered, this, &QtGuiApplication1::OnActionClear);
18 | connect(ui.actionAbout, &QAction::triggered, this, &QtGuiApplication1::OnActionAbout);
19 | updateStatusBar("Drag model file to open");
20 | setAcceptDrops(true);
21 |
22 | SetupPlatformWindow();
23 | }
24 |
25 | void QtGuiApplication1::OnActionOpen()
26 | {
27 | QString filename = QFileDialog::getOpenFileName(this, "Load model file", {}, tr(
28 | "Model file (*.mdl)"
29 | ));
30 | if(!filename.isEmpty())
31 | {
32 | try
33 | {
34 | OpenFile(filename);
35 | }
36 | catch (const std::exception &e)
37 | {
38 | QMessageBox::warning(this, "Error", e.what());
39 | }
40 | }
41 | }
42 |
43 | void QtGuiApplication1::OnActionSave()
44 | {
45 | QString filename = QFileDialog::getSaveFileName(this, "Save model file", {}, tr("Model file (*.mdl)"));
46 | if (!filename.isEmpty())
47 | {
48 | try
49 | {
50 | SaveFile(filename);
51 | }
52 | catch (const std::exception &e)
53 | {
54 | QMessageBox::warning(this, "Error", e.what());
55 | }
56 | }
57 | }
58 |
59 | void QtGuiApplication1::OnActionClear()
60 | {
61 | CloseFile();
62 | }
63 |
64 | void QtGuiApplication1::OnActionAbout()
65 | {
66 | QMessageBox::about(this, "About", "Made by MoeMod in Summer 2019.");
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/src/v/hlmv.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "ui_hlmv.h"
5 |
6 | class QtGuiApplication1 : public QMainWindow
7 | {
8 | Q_OBJECT
9 |
10 | public:
11 | QtGuiApplication1(QWidget *parent = Q_NULLPTR);
12 |
13 | friend class MyOpenGLWidget;
14 |
15 | public slots:
16 | void OnActionOpen();
17 | void OnActionSave();
18 | void OnActionClear();
19 | void OnActionAbout();
20 | void OnTabChanged(int);
21 |
22 | void setSequence(int);
23 | void OnSetAnimationPlaying(bool);
24 | void OnSetAnimationCurrentFrame(int);
25 | void OnSetAnimationFPS(double);
26 |
27 | void setEventInfo(int index);
28 | void setMesh(int index);
29 |
30 | void setTextureCurrent(int index);
31 | void setTextureScale(int value);
32 | void updateTextureFlags();
33 | void updateTextureUVDisplay();
34 | void onImportTexture();
35 | void onExportTexture();
36 |
37 | void setBodypart (int index);
38 | void setSubmodel (int index);
39 | void setSkin (int index);
40 | void setBoneController (int index);
41 | void setBoneControllerCurrentValue (int value);
42 |
43 | void updateViewSettings();
44 | void updateWeaponOriginSettings();
45 |
46 | signals:
47 | void updateStatusBar(QString str);
48 |
49 | protected:
50 | void dragEnterEvent(QDragEnterEvent *e) override;
51 | void dropEvent(QDropEvent *e) override;
52 |
53 | public:
54 | void OpenFile(QString str) noexcept(false);
55 | void SaveFile(QString str) noexcept(false);
56 | void CloseFile();
57 | void initSequences ();
58 | void setSequenceInfo ();
59 | void setEvent (int seq);
60 | void initTextures ();
61 | void initMeshList (int index);
62 | void initBodyparts();
63 | void initBoneControllers ();
64 | void setBoneControllerValue (int index, float value);
65 | void initSkins ();
66 | void setModelInfo ();
67 | void centerView();
68 |
69 | public:
70 | #ifdef Q_OS_MAC
71 | void SetupMacWindow();
72 | void InstallTouchBar();
73 | void SetupPlatformWindow() { SetupMacWindow(); InstallTouchBar(); }
74 | #elif defined(Q_OS_WIN32)
75 | void SetupAeroWindow();
76 | void SetupPlatformWindow() { SetupAeroWindow(); }
77 | #else
78 | void SetupPlatformWindow() { }
79 | #endif
80 |
81 | public:
82 | Ui::QtGuiApplication1Class ui;
83 | };
84 |
--------------------------------------------------------------------------------
/src/v/hlmv.qrc:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/v/myopenglwidget.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "ViewerSettings.h"
14 |
15 | #include "gl_draw.h"
16 | #include "myopenglwidget.h"
17 |
18 | #include "qt_image.h"
19 |
20 | struct MyOpenGLWidget::impl_t
21 | {
22 | explicit impl_t(MyOpenGLWidget *p) : m_Timer(p) {}
23 |
24 | QPoint m_LastMousePos;
25 |
26 | QTimer m_Timer;
27 | CHLMV_GL_Draw m_gldraw;
28 | };
29 |
30 | MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) :
31 | pimpl(new impl_t(this))
32 | {
33 | QObject::connect (&pimpl->m_Timer, SIGNAL (timeout ()), this, SLOT (idleEvent ()));
34 | pimpl->m_Timer.start ();
35 |
36 | grabGesture(Qt::PinchGesture);
37 | //QApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false);
38 | //QApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false);
39 | }
40 |
41 | MyOpenGLWidget::~MyOpenGLWidget() = default;
42 |
43 | void MyOpenGLWidget::idleEvent()
44 | {
45 | pimpl->m_gldraw.Think();
46 |
47 | if (!g_viewerSettings.pause)
48 | this->updateGL ();
49 | }
50 |
51 | void MyOpenGLWidget::initializeGL()
52 | {
53 |
54 | }
55 |
56 | void MyOpenGLWidget::resizeGL(int w, int h)
57 | {
58 | pimpl->m_gldraw.SetSize(width(), height(), w, h);
59 | }
60 |
61 | void MyOpenGLWidget::paintGL()
62 | {
63 | pimpl->m_gldraw.Draw();
64 | }
65 |
66 | bool MyOpenGLWidget::event(QEvent *event)
67 | {
68 | switch(event->type()){
69 | case QEvent::Gesture:
70 | {
71 | gestureEvent(static_cast(event));
72 | return true;
73 | }
74 | case QEvent::TouchBegin:
75 | case QEvent::TouchUpdate:
76 | case QEvent::TouchEnd:
77 | {
78 | touchEvent(static_cast(event));
79 | return true;
80 | }
81 | default:
82 | break;
83 | }
84 | return QGLWidget::event(event);
85 | }
86 |
87 | void MyOpenGLWidget::gestureEvent(QGestureEvent *event)
88 | {
89 | // 2-fingers on both touchpad and touchscreen
90 | QPinchGesture *pinch = static_cast(event->gesture(Qt::PinchGesture));
91 | if (pinch)
92 | {
93 | QPinchGesture::ChangeFlags changeFlags = pinch->changeFlags();
94 | if (changeFlags & QPinchGesture::ScaleFactorChanged)
95 | {
96 | qreal scale_delta = pinch->scaleFactor();
97 | g_viewerSettings.trans[2] /= scale_delta * scale_delta;
98 | }
99 |
100 | if(changeFlags & QPinchGesture::CenterPointChanged)
101 | {
102 | QPointF position_delta = pinch->centerPoint() - pinch->lastCenterPoint();
103 | g_viewerSettings.trans[0] -= position_delta.x() / 4;
104 | g_viewerSettings.trans[1] += position_delta.y() / 4;
105 | }
106 | }
107 |
108 | // 3-fingers
109 | /*
110 | QPanGesture *pan = static_cast(event->gesture(Qt::PanGesture));
111 | if (pan)
112 | {
113 | QPointF position_delta = pan->delta();
114 | g_viewerSettings.trans[0] -= position_delta.x();
115 | g_viewerSettings.trans[1] += position_delta.y();
116 | }
117 | */
118 |
119 | updateGL();
120 | }
121 |
122 | void MyOpenGLWidget::touchEvent(QTouchEvent *event)
123 | {
124 | if (event->touchPoints().size() != 1)
125 | return;
126 |
127 | const QTouchEvent::TouchPoint &tp = event->touchPoints().first();
128 | if (1)
129 | {
130 | auto delta = tp.pos() - tp.lastPos();
131 | g_viewerSettings.rot[0] += delta.y();
132 | g_viewerSettings.rot[1] += delta.x();
133 | updateGL();
134 | }
135 | }
136 |
137 | void MyOpenGLWidget::mousePressEvent(QMouseEvent *event)
138 | {
139 | if (event->source() != Qt::MouseEventNotSynthesized)
140 | return;
141 | pimpl->m_LastMousePos = event->pos();
142 | }
143 |
144 | void MyOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
145 | {
146 | if (event->source() != Qt::MouseEventNotSynthesized)
147 | return;
148 |
149 | int dx = event->x() - pimpl->m_LastMousePos.x();
150 | int dy = event->y() - pimpl->m_LastMousePos.y();
151 |
152 | if (event->buttons() & Qt::LeftButton) {
153 |
154 | g_viewerSettings.rot[0] += dy;
155 | g_viewerSettings.rot[1] += dx;
156 |
157 | updateGL();
158 | }
159 | else if (event->buttons() & Qt::RightButton) {
160 | g_viewerSettings.trans[0] -= dx;
161 | g_viewerSettings.trans[1] += dy;
162 |
163 | updateGL();
164 | }
165 |
166 | pimpl->m_LastMousePos = event->pos();
167 | }
168 |
169 | void MyOpenGLWidget::wheelEvent(QWheelEvent *event)
170 | {
171 | if(event->source() == Qt::MouseEventSynthesizedBySystem)
172 | {
173 | // macOS touchpad
174 | auto delta = event->pixelDelta();
175 | g_viewerSettings.trans[0] -= delta.x();
176 | g_viewerSettings.trans[1] += delta.y();
177 | }
178 | else
179 | {
180 | g_viewerSettings.trans[2] += event->delta();
181 | }
182 | updateGL();
183 | }
184 |
185 | void MyOpenGLWidget::setLight(bool b)
186 | {
187 | b ? glEnable(GL_LIGHTING) : glDisable(GL_LIGHTING);
188 | b ? glEnable(GL_LIGHT0) : glDisable(GL_LIGHT0);
189 | updateGL();
190 | }
191 |
192 | void MyOpenGLWidget::onActionSnapshot()
193 | {
194 | const int w = pimpl->m_gldraw.w2();
195 | const int h = pimpl->m_gldraw.h2();
196 |
197 | QImage qi( w, h, QImage::Format::Format_RGB888);
198 |
199 | updateGL();
200 | glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, qi.bits());
201 |
202 | qi = std::move(qi).mirrored();
203 |
204 | QClipboard *clipboard = QApplication::clipboard();
205 | clipboard->setPixmap(QPixmap::fromImage(qi));
206 |
207 | if(QMessageBox::question(this,
208 | "Save",
209 | "Image has been copied to clipboard.\nDo you want to save it to a file?",
210 | QMessageBox::StandardButtons(QMessageBox::Save | QMessageBox::Cancel)
211 | ) == QMessageBox::Save)
212 | {
213 |
214 | QString filename = getSaveFileImagePath(this, {}, {});
215 |
216 | if(!filename.isEmpty())
217 | try
218 | {
219 | saveImageTo(qi, filename);
220 | }
221 | catch(const std::exception &e)
222 | {
223 | QMessageBox::critical(this, "Error", e.what()), void();
224 | }
225 |
226 | }
227 | }
228 |
229 | void MyOpenGLWidget::setPaused(bool x)
230 | {
231 | pimpl->m_gldraw.SetStopPlaying(x);
232 | }
233 |
--------------------------------------------------------------------------------
/src/v/myopenglwidget.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class QGestureEvent;
7 | class QTouchEvent;
8 |
9 | class MyOpenGLWidget : public QGLWidget
10 | {
11 | Q_OBJECT
12 |
13 | public:
14 | MyOpenGLWidget(QWidget *parent = Q_NULLPTR);
15 | ~MyOpenGLWidget();
16 |
17 | void initializeGL() override;
18 | void resizeGL(int w, int h) override;
19 | void paintGL() override;
20 |
21 | bool event(QEvent *event) override;
22 | void mousePressEvent(QMouseEvent *event) override;
23 | void mouseMoveEvent(QMouseEvent *event) override;
24 | void wheelEvent(QWheelEvent *event) override;
25 |
26 | void gestureEvent(QGestureEvent *event);
27 | void touchEvent(QTouchEvent *event);
28 |
29 | signals: // signals
30 |
31 | public slots:
32 | void setLight(bool);
33 | void onActionSnapshot();
34 | void idleEvent();
35 | void setPaused(bool);
36 |
37 | private:
38 | struct impl_t;
39 | const std::unique_ptr pimpl;
40 |
41 | };
42 |
--------------------------------------------------------------------------------
/src/v/platform/macos/MacWindow.mm:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 小白白 on 2019-06-06.
3 | //
4 |
5 | #include "hlmv.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #import
12 |
13 | #include "ViewerSettings.h"
14 |
15 | @interface MyWindowDelegate : NSObject
16 | - (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
17 | - (BOOL)windowShouldClose:(id)sender;
18 | @end
19 | @implementation MyWindowDelegate
20 | - (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
21 | {
22 | return (NSApplicationPresentationFullScreen |
23 | NSApplicationPresentationHideDock |
24 | NSApplicationPresentationAutoHideMenuBar |
25 | NSApplicationPresentationAutoHideToolbar);
26 | }
27 | - (BOOL)windowShouldClose:(id)sender {
28 | exit(0);
29 | return YES;
30 | }
31 | - (void)windowDidResize:(NSNotification *)notification {
32 | NSWindow *nsw = notification.object;
33 | nsw.styleMask |= NSWindowStyleMaskUnifiedTitleAndToolbar;
34 | nsw.styleMask |= NSWindowStyleMaskFullSizeContentView;
35 | }
36 | @end
37 |
38 | void QtGuiApplication1::SetupMacWindow()
39 | {
40 | ui.statusBar->setVisible(false);
41 |
42 | NSApplication *nsa = NSApp;
43 |
44 | NSWindow* nsw = [reinterpret_cast(this->windowHandle()->winId()) window];
45 |
46 | MyWindowDelegate *mwd = [[MyWindowDelegate alloc] init];
47 | nsw.delegate = mwd;
48 |
49 | nsw.styleMask |= NSWindowStyleMaskUnifiedTitleAndToolbar;
50 | nsw.styleMask |= NSWindowStyleMaskFullSizeContentView;
51 | //nsw.titleVisibility = NSWindowTitleHidden;
52 | nsw.titlebarAppearsTransparent = TRUE;
53 |
54 | QMacToolBar *toolBar = new QMacToolBar(this);
55 | NSToolbar *nst = toolBar->nativeToolbar();
56 | nst.displayMode = NSToolbarDisplayModeLabelOnly;
57 | nst.sizeMode = NSToolbarSizeModeRegular;
58 |
59 | // pages
60 | toolBar->addStandardItem(QMacToolBarItem::FlexibleSpace);
61 | toolBar->addStandardItem(QMacToolBarItem::Space);
62 | toolBar->addSeparator();
63 | QTabWidget *tabWidget = ui.tabWidget;
64 | tabWidget->setVisible(false);
65 | tabWidget->tabBar()->setVisible(false);
66 |
67 | for(int i=0;icount();++i)
68 | {
69 | QMacToolBarItem *item = toolBar->addItem(tabWidget->tabIcon(i), tabWidget->tabText(i));
70 | item->setSelectable(true);
71 | NSToolbarItem *nativeItem = item->nativeToolBarItem();
72 |
73 | if(tabWidget->currentIndex() == i)
74 | {
75 | [nst setSelectedItemIdentifier:[nativeItem itemIdentifier]];
76 | }
77 |
78 | auto act_lambda = [=]() {
79 | if(tabWidget->currentIndex() == i) {
80 | if(!tabWidget->isVisible() ) {
81 | tabWidget->setVisible(true);
82 | ui.statusBar->setVisible(true);
83 | }
84 | else {
85 | tabWidget->setVisible(false);
86 | ui.statusBar->setVisible(false);
87 | }
88 |
89 | //[nst setSelectedItemIdentifier:nil];
90 | }
91 | else {
92 | tabWidget->setCurrentWidget(tabWidget->widget(i));
93 | }
94 | };
95 |
96 | connect(item,&QMacToolBarItem::activated, act_lambda);
97 | }
98 | toolBar->addSeparator();
99 | toolBar->addStandardItem(QMacToolBarItem::FlexibleSpace);
100 | toolBar->addStandardItem(QMacToolBarItem::Space);
101 |
102 |
103 | toolBar->attachToWindow(this->window()->windowHandle());
104 |
105 |
106 | NSButton *closeBtn = [nsw standardWindowButton:NSWindowCloseButton];
107 | NSButton *miniaturizeBtn = [nsw standardWindowButton:NSWindowMiniaturizeButton];
108 | NSButton *zoomBtn = [nsw standardWindowButton:NSWindowZoomButton];
109 |
110 | closeBtn.translatesAutoresizingMaskIntoConstraints = NO;
111 | miniaturizeBtn.translatesAutoresizingMaskIntoConstraints = NO;
112 | zoomBtn.translatesAutoresizingMaskIntoConstraints = NO;
113 |
114 | NSLayoutConstraint *leftContraint1 = [NSLayoutConstraint constraintWithItem:closeBtn attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:closeBtn.superview attribute:NSLayoutAttributeLeft multiplier:1.0 constant:14.0];
115 | NSLayoutConstraint *topContraint1 = [NSLayoutConstraint constraintWithItem:closeBtn attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:closeBtn.superview attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
116 | leftContraint1.active = YES;
117 | topContraint1.active = YES;
118 |
119 | NSLayoutConstraint *leftContraint2 = [NSLayoutConstraint constraintWithItem:miniaturizeBtn attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:miniaturizeBtn.superview attribute:NSLayoutAttributeLeft multiplier:1.0 constant:33.0];
120 | NSLayoutConstraint *topContraint2 = [NSLayoutConstraint constraintWithItem:miniaturizeBtn attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:miniaturizeBtn.superview attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
121 | leftContraint2.active = YES;
122 | topContraint2.active = YES;
123 |
124 | NSLayoutConstraint *leftContraint3 = [NSLayoutConstraint constraintWithItem:zoomBtn attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:zoomBtn.superview attribute:NSLayoutAttributeLeft multiplier:1.0 constant:53.0];
125 | NSLayoutConstraint *topContraint3 = [NSLayoutConstraint constraintWithItem:zoomBtn attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:zoomBtn.superview attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
126 | leftContraint3.active = YES;
127 | topContraint3.active = YES;
128 |
129 | }
130 |
131 |
--------------------------------------------------------------------------------
/src/v/platform/macos/Touchbar.mm:
--------------------------------------------------------------------------------
1 | //
2 | // Created by MoeMod on 2019-06-18.
3 | //
4 |
5 | #import
6 | #import
7 | #include "hlmv.h"
8 |
9 | // This example shows how to create and populate touch bars for Qt applications.
10 | // Two approaches are demonstrated: creating a global touch bar for the entire
11 | // application via the NSApplication delegate, and creating per-window touch bars
12 | // via the NSWindow delegate. Applications may use either or both of these, for example
13 | // to provide global base touch bar with window specific additions. Refer to the
14 | // NSTouchBar documentation for further details.
15 |
16 | static NSTouchBarItemIdentifier TextItemIdentifier = @"com.myapp.TextItemIdentifier";
17 |
18 | @interface ViewModeScrubberDelegate : NSResponder
19 | - (instancetype) initWithHLMV:(QtGuiApplication1 *)hlmv;
20 | @property QtGuiApplication1 *hlmv;
21 | @property (strong) NSArray *validItems;
22 | @end
23 | @implementation ViewModeScrubberDelegate
24 |
25 | - (instancetype) initWithHLMV:(QtGuiApplication1 *)hlmv {
26 | [super init];
27 | _hlmv = hlmv;
28 | NSWindow* nsw = [reinterpret_cast(self.hlmv->windowHandle()->winId()) window];
29 | NSToolbar *nst = nsw.toolbar;
30 |
31 | NSArray *items = [nst.items copy];
32 | NSMutableArray *new_items = [[NSMutableArray alloc] init];
33 |
34 | int iSelected = 0;
35 | for(int i = 0; i < items.count; ++i)
36 | {
37 | NSToolbarItem * item = items[i];
38 | if(item.action == nil)
39 | continue;
40 | [new_items addObject:item];
41 | if([nst.selectedItemIdentifier isEqualToString:[item itemIdentifier]])
42 | {
43 |
44 | }
45 | }
46 |
47 |
48 | _validItems = new_items;
49 |
50 | return self;
51 | }
52 |
53 | - (NSInteger)numberOfItemsForScrubber:(NSScrubber *)scrubber {
54 | return [_validItems count];
55 | }
56 |
57 | - (NSScrubberItemView *)scrubber:(NSScrubber *)scrubber viewForItemAtIndex:(NSInteger)index {
58 | NSScrubberTextItemView *view = [scrubber makeItemWithIdentifier:TextItemIdentifier owner:nil];
59 |
60 | view.textField.stringValue = _validItems[index].label;
61 |
62 | return view;
63 | }
64 |
65 | - (NSSize)scrubber:(NSScrubber *)scrubber layout:(NSScrubberFlowLayout *)layout sizeForItemAtIndex:(NSInteger)index {
66 | QTabWidget *tabWidget = self.hlmv->ui.tabWidget;
67 | NSString *string = tabWidget->tabText(static_cast(index)).toNSString();
68 | NSRect bounds = [string boundingRectWithSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)
69 | options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
70 | attributes:@{NSFontAttributeName: [NSFont systemFontOfSize:0]}];
71 |
72 | return NSMakeSize(bounds.size.width + 50, 30);
73 | }
74 |
75 | - (void)didFinishInteractingWithScrubber:(NSScrubber *)scrubber {
76 |
77 | auto selectedIndex = scrubber.selectedIndex;
78 | QTabWidget *tabWidget = self.hlmv->ui.tabWidget;
79 |
80 | NSWindow* nsw = [reinterpret_cast(self.hlmv->windowHandle()->winId()) window];
81 | NSToolbar *nst = nsw.toolbar;
82 | NSToolbarItem *nativeItem = _validItems[selectedIndex];
83 | [nst setSelectedItemIdentifier:[nativeItem itemIdentifier]];
84 |
85 | tabWidget->setCurrentWidget(tabWidget->widget(static_cast(selectedIndex)));
86 |
87 | }
88 | - (void)dealloc {
89 | [_validItems release];
90 | [super dealloc];
91 | }
92 | @end
93 |
94 | @interface SyncComboBoxScrubberDelegate : NSResponder
95 | - (instancetype) initWithComboBox:(QComboBox *)combo;
96 | @property QComboBox *combo;
97 | @end
98 | @implementation SyncComboBoxScrubberDelegate
99 |
100 | - (instancetype) initWithComboBox:(QComboBox *)combo {
101 | [super init];
102 | _combo = combo;
103 |
104 | return self;
105 | }
106 |
107 | - (NSInteger)numberOfItemsForScrubber:(NSScrubber *)scrubber {
108 | return _combo->count();
109 | }
110 |
111 | - (NSScrubberItemView *)scrubber:(NSScrubber *)scrubber viewForItemAtIndex:(NSInteger)index {
112 | NSScrubberTextItemView *view = [scrubber makeItemWithIdentifier:TextItemIdentifier owner:nil];
113 | view.title = _combo->itemText(index).toNSString();
114 | return view;
115 | }
116 |
117 | - (NSSize)scrubber:(NSScrubber *)scrubber layout:(NSScrubberFlowLayout *)layout sizeForItemAtIndex:(NSInteger)index {
118 | NSString *string = _combo->itemText(index).toNSString();
119 | NSRect bounds = [string boundingRectWithSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)
120 | options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
121 | attributes:@{NSFontAttributeName: [NSFont systemFontOfSize:0]}];
122 |
123 | return NSMakeSize(bounds.size.width + 20, 30);
124 | }
125 |
126 | - (void)scrubber:(NSScrubber *)scrubber didSelectItemAtIndex:(NSInteger)selectedIndex {
127 |
128 | _combo->setCurrentIndex(selectedIndex);
129 |
130 | }
131 | @end
132 |
133 | @interface SyncComboBoxScrubberTouchBarItem: NSCustomTouchBarItem
134 | - (instancetype) initWithIdentifier:identifier ComboBox:(QComboBox *)combo;
135 | @property (strong) NSScrubber *scrubber;
136 | @property QComboBox *combo;
137 | @property QMetaObject::Connection connection;
138 | @end
139 | @implementation SyncComboBoxScrubberTouchBarItem
140 |
141 | - (instancetype) initWithIdentifier:identifier ComboBox:(QComboBox *)combo {
142 | self = [super initWithIdentifier:identifier];
143 | NSScrubber *scrubber = [[[NSScrubber alloc] init] autorelease];
144 | scrubber.scrubberLayout = [[NSScrubberFlowLayout alloc] init];
145 | scrubber.mode = NSScrubberModeFree;
146 | scrubber.continuous = false;
147 | scrubber.selectionBackgroundStyle = [NSScrubberSelectionStyle outlineOverlayStyle];
148 |
149 | SyncComboBoxScrubberDelegate *scrubberDelegate = [[SyncComboBoxScrubberDelegate alloc] initWithComboBox:combo];
150 | scrubber.delegate = scrubberDelegate;
151 | scrubber.dataSource = scrubberDelegate;
152 | scrubber.floatsSelectionViews = false;
153 | scrubber.selectedIndex = combo->currentIndex();
154 |
155 | [scrubber registerClass:[NSScrubberTextItemView class] forItemIdentifier:TextItemIdentifier];
156 |
157 | self.view = _scrubber = scrubber;
158 | _combo = combo;
159 | _connection = QObject::connect(combo,
160 | static_cast(&QComboBox::currentIndexChanged),
161 | [self]{ _scrubber.selectedIndex =_combo->currentIndex(); }
162 | );
163 | return self;
164 | }
165 |
166 | @end
167 |
168 | // The TouchBarProvider class implements the NSTouchBarDelegate protocol, as
169 | // well as app and window delegate protocols.
170 | @interface TouchBarProvider: NSResponder
171 |
172 | @property (strong) NSCustomTouchBarItem *touchBarItem1;
173 | @property (strong) NSCustomTouchBarItem *touchBarItem2;
174 | @property (strong) NSCustomTouchBarItem *touchBarItem3;
175 | @property (strong) NSCustomTouchBarItem *touchBarItem4;
176 |
177 | @property (strong) NSButton *touchBarButton1;
178 | @property (strong) NSButton *touchBarButton2;
179 | @property (strong) NSButton *touchBarButton3;
180 | @property (strong) NSButton *touchBarButton4;
181 |
182 | @property (strong) NSObject *qtDelegate;
183 |
184 | @property QtGuiApplication1 *hlmv;
185 |
186 | - (instancetype) initWithHLMVInstance:(QtGuiApplication1 *)p;
187 |
188 | @end
189 |
190 | // Create identifiers for two button items.
191 | static NSTouchBarItemIdentifier Button1Identifier = @"com.myapp.Button1Identifier";
192 | static NSTouchBarItemIdentifier Button2Identifier = @"com.myapp.Button2Identifier";
193 | static NSTouchBarItemIdentifier Button3Identifier = @"com.myapp.Button3Identifier";
194 | static NSTouchBarItemIdentifier Button4Identifier = @"com.myapp.Button4Identifier";
195 | static NSTouchBarItemIdentifier ViewModeScrubberItemIdentifier = @"com.hlmv.ViewModeScrubberItemIdentifier";
196 | static NSTouchBarItemIdentifier ViewModeItemIdentifier = @"com.hlmv.ViewModeItemIdentifier";
197 | static NSTouchBarItemIdentifier SequenceScrubberItemIdentifier = @"com.hlmv.SequenceScrubberItemIdentifier";
198 | static NSTouchBarItemIdentifier TextureItemIdentifier = @"com.hlmv.TextureItemIdentifier";
199 | static NSTouchBarItemIdentifier TextureScrubberItemIdentifier = @"com.hlmv.TextureScrubberItemIdentifier";
200 | static NSTouchBarItemIdentifier BodyPartItemIdentifier = @"com.hlmv.BodyPartItemIdentifier";
201 | static NSTouchBarItemIdentifier BodyPartScrubberItemIdentifier = @"com.hlmv.BodyPartScrubberItemIdentifier";
202 | static NSTouchBarItemIdentifier SubModelItemIdentifier = @"com.hlmv.SubModelItemIdentifier";
203 | static NSTouchBarItemIdentifier SubModelScrubberItemIdentifier = @"com.hlmv.SubModelScrubberItemIdentifier";
204 | static NSTouchBarItemIdentifier SkinItemIdentifier = @"com.hlmv.SkinItemIdentifier";
205 | static NSTouchBarItemIdentifier SkinScrubberItemIdentifier = @"com.hlmv.SkinScrubberItemIdentifier";
206 |
207 | @implementation TouchBarProvider
208 |
209 | - (instancetype) initWithHLMVInstance:(QtGuiApplication1 *)p
210 | {
211 | self = [super init];
212 |
213 | self.hlmv = p;
214 |
215 | return self;
216 | }
217 |
218 | - (NSTouchBar *)makeTouchBar
219 | {
220 | // Create the touch bar with this instance as its delegate
221 | NSTouchBar *bar = [[NSTouchBar alloc] init];
222 | bar.delegate = self;
223 |
224 | // Add touch bar items: first, the very important emoji picker, followed
225 | // by two buttons. Note that no further handling of the emoji picker
226 | // is needed (emojii are automatically routed to any active text edit). Button
227 | // actions handlers are set up in makeItemForIdentifier below.
228 | if(_hlmv->ui.tabWidget->currentIndex() == 3 || _hlmv->ui.tabWidget->currentIndex() == 4) // view origin
229 | {
230 | bar.defaultItemIdentifiers = @[\
231 | ViewModeItemIdentifier,
232 | NSTouchBarItemIdentifierFixedSpaceLarge,
233 | SequenceScrubberItemIdentifier
234 | ];
235 | }
236 | else if(_hlmv->ui.tabWidget->currentIndex() == 2) // texture
237 | {
238 | bar.defaultItemIdentifiers = @[\
239 | ViewModeItemIdentifier,
240 | NSTouchBarItemIdentifierFixedSpaceLarge,
241 | TextureItemIdentifier
242 | ];
243 | }
244 | else if(_hlmv->ui.tabWidget->currentIndex() == 1) // body part
245 | {
246 | bar.defaultItemIdentifiers = @[\
247 | ViewModeItemIdentifier,
248 | NSTouchBarItemIdentifierFixedSpaceLarge,
249 | BodyPartItemIdentifier,
250 | SubModelItemIdentifier,
251 | SkinItemIdentifier,
252 | ];
253 | }
254 | else
255 | {
256 | bar.defaultItemIdentifiers = @[\
257 | ViewModeScrubberItemIdentifier
258 | ];
259 | }
260 |
261 |
262 | return bar;
263 | }
264 |
265 | - (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
266 | {
267 | Q_UNUSED(touchBar);
268 |
269 | // Create touch bar items as NSCustomTouchBarItems which can contain any NSView.
270 | if ([identifier isEqualToString:ViewModeItemIdentifier]) {
271 | NSPopoverTouchBarItem *item = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier];
272 |
273 | QTabBar *tabBar = _hlmv->ui.tabWidget->tabBar();
274 | item.collapsedRepresentationLabel = tabBar->tabText(tabBar->currentIndex()).toNSString();
275 | item.collapsedRepresentationImage = [NSImage imageNamed:NSImageNameTouchBarSidebarTemplate];
276 |
277 | NSTouchBar *secondaryTouchBar = [[NSTouchBar alloc] init];
278 | secondaryTouchBar.delegate = self;
279 | secondaryTouchBar.defaultItemIdentifiers = @[ViewModeScrubberItemIdentifier];
280 |
281 | item.popoverTouchBar = secondaryTouchBar;
282 |
283 | return item;
284 | }
285 | else if ([identifier isEqualToString:ViewModeScrubberItemIdentifier]) {
286 |
287 | ViewModeScrubberDelegate *scrubberDelegate = [[ViewModeScrubberDelegate alloc] initWithHLMV:_hlmv];
288 | //scrubberDelegate.hlmv = hlmv;
289 |
290 | NSScrubber *scrubber = [[[NSScrubber alloc] init] autorelease];
291 | scrubber.scrubberLayout = [[NSScrubberFlowLayout alloc] init];
292 | //scrubber.mode = NSScrubberModeFree;
293 | scrubber.continuous = true;
294 | scrubber.selectionBackgroundStyle = [NSScrubberSelectionStyle outlineOverlayStyle];
295 | scrubber.delegate = scrubberDelegate;
296 | scrubber.dataSource = scrubberDelegate;
297 | scrubber.floatsSelectionViews = false;
298 | scrubber.selectedIndex = _hlmv->ui.tabWidget->currentIndex();
299 |
300 | [scrubber registerClass:[NSScrubberTextItemView class] forItemIdentifier:TextItemIdentifier];
301 |
302 |
303 | NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
304 | item.view = scrubber;
305 |
306 | return item;
307 | }
308 | else if ([identifier isEqualToString:TextureItemIdentifier]) {
309 | NSPopoverTouchBarItem *item = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier];
310 | item.collapsedRepresentationLabel = _hlmv->ui.cTextures->currentText().toNSString();
311 | item.collapsedRepresentationImage = [NSImage imageNamed:NSImageNameTouchBarQuickLookTemplate];
312 | NSTouchBar *secondaryTouchBar = [[NSTouchBar alloc] init];
313 | secondaryTouchBar.delegate = self;
314 | secondaryTouchBar.defaultItemIdentifiers = @[TextureScrubberItemIdentifier];
315 | item.popoverTouchBar = secondaryTouchBar;
316 | return item;
317 | }
318 | else if ([identifier isEqualToString:TextureScrubberItemIdentifier]) {
319 | return [[SyncComboBoxScrubberTouchBarItem alloc] initWithIdentifier:identifier ComboBox:_hlmv->ui.cTextures];
320 | }
321 | else if ([identifier isEqualToString:BodyPartItemIdentifier]) {
322 | NSPopoverTouchBarItem *item = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier];
323 | item.collapsedRepresentationLabel = _hlmv->ui.cBodypart->currentText().toNSString();
324 | item.collapsedRepresentationImage = [NSImage imageNamed:NSImageNameTouchBarColorPickerFill];
325 | NSTouchBar *secondaryTouchBar = [[NSTouchBar alloc] init];
326 | secondaryTouchBar.delegate = self;
327 | secondaryTouchBar.defaultItemIdentifiers = @[BodyPartScrubberItemIdentifier];
328 | item.popoverTouchBar = secondaryTouchBar;
329 | return item;
330 | }
331 | else if ([identifier isEqualToString:BodyPartScrubberItemIdentifier]) {
332 | return [[SyncComboBoxScrubberTouchBarItem alloc] initWithIdentifier:identifier ComboBox:_hlmv->ui.cBodypart];
333 | }
334 | else if ([identifier isEqualToString:SubModelItemIdentifier]) {
335 | NSPopoverTouchBarItem *item = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier];
336 | item.collapsedRepresentationLabel = _hlmv->ui.cSubmodel->currentText().toNSString();
337 | item.collapsedRepresentationImage = [NSImage imageNamed:NSImageNameTouchBarRecordStartTemplate];
338 | NSTouchBar *secondaryTouchBar = [[NSTouchBar alloc] init];
339 | secondaryTouchBar.delegate = self;
340 | secondaryTouchBar.defaultItemIdentifiers = @[SubModelScrubberItemIdentifier];
341 | item.popoverTouchBar = secondaryTouchBar;
342 | return item;
343 | }
344 | else if ([identifier isEqualToString:SubModelScrubberItemIdentifier]) {
345 | return [[SyncComboBoxScrubberTouchBarItem alloc] initWithIdentifier:identifier ComboBox:_hlmv->ui.cSubmodel];
346 | }
347 | else if ([identifier isEqualToString:SkinItemIdentifier]) {
348 | NSPopoverTouchBarItem *item = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier];
349 | item.collapsedRepresentationLabel = _hlmv->ui.cSkin->currentText().toNSString();
350 | item.collapsedRepresentationImage = [NSImage imageNamed:NSImageNameTouchBarColorPickerStroke];
351 | NSTouchBar *secondaryTouchBar = [[NSTouchBar alloc] init];
352 | secondaryTouchBar.delegate = self;
353 | secondaryTouchBar.defaultItemIdentifiers = @[SkinScrubberItemIdentifier];
354 | item.popoverTouchBar = secondaryTouchBar;
355 | return item;
356 | }
357 | else if ([identifier isEqualToString:SkinScrubberItemIdentifier]) {
358 | return [[SyncComboBoxScrubberTouchBarItem alloc] initWithIdentifier:identifier ComboBox:_hlmv->ui.cSkin];
359 | }
360 | else if ([identifier isEqualToString:SequenceScrubberItemIdentifier]) {
361 | return [[SyncComboBoxScrubberTouchBarItem alloc] initWithIdentifier:identifier ComboBox:_hlmv->ui.cWpSequence];
362 | }
363 | else if ([identifier isEqualToString:Button1Identifier]) {
364 | QString title = "Welcome";
365 | self.touchBarItem1 = [[[NSCustomTouchBarItem alloc] initWithIdentifier:identifier] autorelease];
366 | self.touchBarButton1 = [[NSButton buttonWithTitle:title.toNSString() target:self
367 | action:@selector(button1Clicked)] autorelease];
368 | self.touchBarButton1.imageHugsTitle = true;
369 | self.touchBarItem1.view = self.touchBarButton1;
370 | return self.touchBarItem1;
371 | }
372 | else if ([identifier isEqualToString:Button2Identifier]) {
373 | QString title = "New Game";
374 | self.touchBarItem2 = [[[NSCustomTouchBarItem alloc] initWithIdentifier:identifier] autorelease];
375 | self.touchBarButton2 = [[NSButton buttonWithTitle:title.toNSString() target:self
376 | action:@selector(button2Clicked)] autorelease];
377 | self.touchBarButton2.imageHugsTitle = true;
378 | self.touchBarItem2.view = self.touchBarButton2;
379 | return self.touchBarItem2;
380 | }
381 | else if ([identifier isEqualToString:Button3Identifier]) {
382 | QString title = "Servers";
383 | self.touchBarItem3 = [[[NSCustomTouchBarItem alloc] initWithIdentifier:identifier] autorelease];
384 | self.touchBarButton3 = [[NSButton buttonWithTitle:title.toNSString() target:self
385 | action:@selector(button3Clicked)] autorelease];
386 | self.touchBarButton3.imageHugsTitle = true;
387 | self.touchBarItem3.view = self.touchBarButton3;
388 | return self.touchBarItem3;
389 | }
390 | else if ([identifier isEqualToString:Button4Identifier]) {
391 | QString title = "Settings";
392 | self.touchBarItem4 = [[[NSCustomTouchBarItem alloc] initWithIdentifier:identifier] autorelease];
393 | self.touchBarButton4 = [[NSButton buttonWithTitle:title.toNSString() target:self
394 | action:@selector(button4Clicked)] autorelease];
395 | self.touchBarButton4.imageHugsTitle = true;
396 | self.touchBarItem4.view = self.touchBarButton4;
397 | return self.touchBarItem4;
398 | }
399 | return nil;
400 | }
401 |
402 | - (void)installAsDelegateForWindow:(NSWindow *)window
403 | {
404 | _qtDelegate = window.delegate; // Save current delegate for forwarding
405 | window.delegate = self;
406 | }
407 |
408 | - (void)installAsDelegateForApplication:(NSApplication *)application
409 | {
410 | _qtDelegate = application.delegate; // Save current delegate for forwarding
411 | application.delegate = self;
412 | }
413 |
414 | - (BOOL)respondsToSelector:(SEL)aSelector
415 | {
416 | // We want to forward to the qt delegate. Respond to selectors it
417 | // responds to in addition to selectors this instance resonds to.
418 | return [_qtDelegate respondsToSelector:aSelector] || [super respondsToSelector:aSelector];
419 | }
420 |
421 | - (void)forwardInvocation:(NSInvocation *)anInvocation
422 | {
423 | // Forward to the existing delegate. This function is only called for selectors
424 | // this instance does not responds to, which means that the qt delegate
425 | // must respond to it (due to the respondsToSelector implementation above).
426 | [anInvocation invokeWithTarget:_qtDelegate];
427 | }
428 |
429 | - (void)button1Clicked
430 | {
431 | [[NSApplication sharedApplication] toggleTouchBarCustomizationPalette:nil];
432 |
433 | }
434 |
435 | - (void)button2Clicked
436 | {
437 |
438 | }
439 |
440 | - (void)button3Clicked
441 | {
442 |
443 | }
444 |
445 | - (void)button4Clicked
446 | {
447 |
448 | }
449 |
450 | @end
451 |
452 | void QtGuiApplication1::InstallTouchBar()
453 | {
454 | NSWindow* nsw = [reinterpret_cast(this->windowHandle()->winId()) window];
455 | if(nsw.touchBar)
456 | {
457 | nsw.touchBar = nil;
458 | }
459 | else
460 | {
461 | // Install TouchBarProvider as application delegate
462 | TouchBarProvider *touchBarProvider = [[TouchBarProvider alloc] initWithHLMVInstance:this];
463 | //[touchBarProvider installAsDelegateForApplication:[NSApplication sharedApplication]];
464 | [touchBarProvider installAsDelegateForWindow:nsw];
465 | }
466 | }
467 |
--------------------------------------------------------------------------------
/src/v/platform/win32/AeroWindow.cpp:
--------------------------------------------------------------------------------
1 | #include "hlmv.h"
2 | #include
3 | #include
4 |
5 | void QtGuiApplication1::SetupAeroWindow()
6 | {
7 | //this->menuBar()->setVisible(false);
8 | //ui.centralWidget->layout()->addItem(ui.centralWidget->layout()->takeAt(0));
9 | if (QOperatingSystemVersion::current() <= QOperatingSystemVersion::Windows7)
10 | {
11 | if (QtWin::isCompositionEnabled())
12 | {
13 | QtWin::extendFrameIntoClientArea(this, -1, -1, -1, -1);
14 | setAttribute(Qt::WA_TranslucentBackground, true);
15 | setAttribute(Qt::WA_NoSystemBackground, false);
16 | }
17 | else
18 | {
19 | setAttribute(Qt::WA_TranslucentBackground, false);
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------