30 ModelWindow(
const std::string filename,
bool usingDefaultModel,
const std::string &path,
const std::string &resource,
const std::string &resource_path,
const std::string &fragmentShaderPath,
const std::string &texture_path,
const std::string &title,
int width,
int height,
bool fullscreen,
bool enable_vsync,
bool binaryTextureMode,
int fontSize,
const std::string &fontPath,
const std::string &color) :
mxvk::
VK_Window(title, width, height, fullscreen,
MXVK_VALIDATION, enable_vsync), assetRoot(path.empty() ? std::string(MODEL_EXAMPLE_ASSET_DIR) : path), binaryTextureMode(binaryTextureMode) {
31 const std::string modelPath = filename;
32 const std::string textureManifestPath = resource.empty() && usingDefaultModel ? assetRoot +
"/data/texture_manifest.txt" : resource;
33 const bool useDefaultTextureBase = resource_path.empty() && (usingDefaultModel || !resource.empty());
34 const std::string textureBasePath = resource_path.empty() ? (useDefaultTextureBase ? assetRoot +
"/data" :
"") : resource_path;
35 const std::string vertPath = assetRoot +
"/data/model.vert.spv";
36 const std::string fragPath = fragmentShaderPath.empty() ? (assetRoot +
"/data/model.frag.spv") : fragmentShaderPath;
37 if (!texture_path.empty())
39 model.load(
this, modelPath, textureManifestPath, textureBasePath, 1.0f);
40 model.setShaders(
this, vertPath, fragPath);
41 model.setBackfaceCulling(!binaryTextureMode);
42 if (binaryTextureMode) {
46 rainConfig.
font_size = std::max(1, fontSize);
47 if (!fontPath.empty()) {
50 rainConfig.
color = color;
51 binaryMatrixTexture = std::make_unique<matrix::Rain>(std::move(rainConfig));
56 if (
device != VK_NULL_HANDLE) {
62 void event(SDL_Event &e)
override {
63 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
68 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_SPACE) {
72 autoSpinEnabled = !autoSpinEnabled;
76 if (e.type == SDL_EVENT_KEY_DOWN && (e.key.key == SDLK_RETURN || e.key.key == SDLK_KP_ENTER)) {
80 skyboxMode = !skyboxMode;
81 model.setBackfaceCulling(!(binaryTextureMode || skyboxMode));
88 if (e.type == SDL_EVENT_KEY_DOWN || e.type == SDL_EVENT_KEY_UP) {
89 const bool pressed = (e.type == SDL_EVENT_KEY_DOWN);
90 if (e.key.key == SDLK_W) {
91 skyboxLookUpKey = pressed;
92 }
else if (e.key.key == SDLK_S) {
93 skyboxLookDownKey = pressed;
94 }
else if (e.key.key == SDLK_A) {
95 skyboxLookLeftKey = pressed;
96 }
else if (e.key.key == SDLK_D) {
97 skyboxLookRightKey = pressed;
98 }
else if (e.key.key == SDLK_UP) {
100 }
else if (e.key.key == SDLK_DOWN) {
101 zoomOutKey = pressed;
105 if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
106 mouseDragging =
true;
107 lastMouseX =
static_cast<int>(e.button.x);
108 lastMouseY =
static_cast<int>(e.button.y);
112 if (e.type == SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT) {
113 mouseDragging =
false;
117 if (e.type == SDL_EVENT_MOUSE_MOTION && mouseDragging) {
118 const int x =
static_cast<int>(e.motion.x);
119 const int y =
static_cast<int>(e.motion.y);
120 const int deltaX = x - lastMouseX;
121 const int deltaY = y - lastMouseY;
124 skyboxYawDegrees +=
static_cast<float>(deltaX) * mouseSensitivity;
125 skyboxPitchDegrees +=
static_cast<float>(deltaY) * mouseSensitivity;
126 skyboxPitchDegrees = std::fmod(skyboxPitchDegrees, 360.0f);
128 yawDegrees +=
static_cast<float>(deltaX) * mouseSensitivity;
129 pitchDegrees +=
static_cast<float>(deltaY) * mouseSensitivity;
130 pitchDegrees = std::clamp(pitchDegrees, -80.0f, 80.0f);
138 if (e.type == SDL_EVENT_MOUSE_WHEEL) {
139 const float delta = (e.wheel.y != 0.0f) ? e.wheel.y :
static_cast<float>(e.wheel.integer_y);
141 const float zoomSpeed = 0.45f;
142 skyboxCameraPosition += skyboxForwardVector() * (delta * zoomSpeed);
144 const float maxSkyboxDistance = 12.0f;
145 const float skyboxDistance = glm::length(skyboxCameraPosition);
146 if (skyboxDistance > maxSkyboxDistance) {
147 skyboxCameraPosition = glm::normalize(skyboxCameraPosition) * maxSkyboxDistance;
150 adjustZoomTarget(delta);
159 const auto now = std::chrono::steady_clock::now();
160 const float deltaSeconds = std::chrono::duration<float>(now - lastFrameTime).count();
161 const float elapsedSeconds = std::chrono::duration<float>(now - startTime).count();
163 if (autoSpinEnabled) {
164 autoSpinRadians += deltaSeconds * autoSpinSpeed;
168 updateSkyboxCamera(deltaSeconds);
170 updateZoom(deltaSeconds);
175 const float aspect = (extent.height > 0U) ?
static_cast<float>(extent.width) /
static_cast<float>(extent.height) : 1.0f;
179 const glm::vec3 front = skyboxForwardVector();
180 const glm::vec3 target = skyboxCameraPosition + front;
181 ubo.
model = glm::scale(glm::mat4(1.0f), glm::vec3(
model.modelRenderScale() * skyboxScaleMultiplier));
183 ubo.
view = glm::lookAt(skyboxCameraPosition, target, skyboxUpVector());
184 ubo.
proj = glm::perspective(glm::radians(70.0f), aspect, 0.02f, 100.0f);
186 ubo.
model = glm::rotate(glm::mat4(1.0f), glm::radians(pitchDegrees), glm::vec3(1.0f, 0.0f, 0.0f));
187 ubo.
model = glm::rotate(ubo.
model, glm::radians(yawDegrees) + autoSpinRadians, glm::vec3(0.0f, 1.0f, 0.0f));
188 ubo.
model = glm::scale(ubo.
model, glm::vec3(
model.modelRenderScale()));
190 ubo.
view = glm::lookAt(glm::vec3(0.0f, 0.0f, cameraDistance), glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
191 ubo.
proj = glm::perspective(glm::radians(50.0f), aspect, 0.1f, 100.0f);
193 ubo.
proj[1][1] *= -1.0f;
194 ubo.
fx = glm::vec4(elapsedSeconds, 0.0f, 0.0f, 0.37f);
199 background->clearQueue();
202 model.updateUBO(imageIndex, ubo);
203 if (binaryTextureMode && binaryMatrixTexture !=
nullptr) {
204 binaryMatrixTexture->update(deltaSeconds);
205 const int textureWidth = binaryMatrixTexture->width();
206 const int textureHeight = binaryMatrixTexture->height();
207 const int texturePitch = binaryMatrixTexture->pitch();
208 const void *texturePixels = flippedMatrixTexturePixels(textureWidth, textureHeight, texturePitch);
209 [[maybe_unused]]
const bool textureUpdated =
model.updatePrimaryTexture(texturePixels, textureWidth, textureHeight, textureWidth * 4);
211 model.render(cmd, imageIndex,
false);
215 std::string assetRoot;
218 bool binaryTextureMode =
false;
219 std::unique_ptr<matrix::Rain> binaryMatrixTexture{};
220 bool mouseDragging =
false;
221 bool skyboxMode =
false;
222 bool skyboxLookUpKey =
false;
223 bool skyboxLookDownKey =
false;
224 bool skyboxLookLeftKey =
false;
225 bool skyboxLookRightKey =
false;
226 bool autoSpinEnabled =
true;
229 float yawDegrees = 0.0f;
230 float pitchDegrees = 12.0f;
231 float cameraDistance = 4.2f;
232 float targetCameraDistance = 4.2f;
233 glm::vec3 skyboxCameraPosition{0.0f, 0.0f, 0.0f};
234 float skyboxYawDegrees = 180.0f;
235 float skyboxPitchDegrees = 0.0f;
236 float skyboxScaleMultiplier = 1.6f;
237 float mouseSensitivity = 0.35f;
238 float autoSpinSpeed = 0.65f;
239 float autoSpinRadians = 0.0f;
240 std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
241 std::chrono::steady_clock::time_point lastFrameTime = std::chrono::steady_clock::now();
242 std::vector<Uint8> flippedMatrixTexture;
243 bool zoomInKey =
false;
244 bool zoomOutKey =
false;
246 void adjustZoomTarget(
float wheelDelta) {
247 if (wheelDelta == 0.0f) {
251 constexpr float zoomStep = 1.12f;
252 targetCameraDistance *= std::pow(zoomStep, -wheelDelta);
253 targetCameraDistance = std::clamp(targetCameraDistance, 1.8f, 12.0f);
256 void updateZoom(
float deltaSeconds) {
257 const float keyDelta = (zoomInKey ? 1.0f : 0.0f) - (zoomOutKey ? 1.0f : 0.0f);
258 if (keyDelta != 0.0f) {
259 constexpr float keyZoomStep = 1.09f;
260 targetCameraDistance *= std::pow(keyZoomStep, -keyDelta * deltaSeconds * 60.0f);
261 targetCameraDistance = std::clamp(targetCameraDistance, 1.8f, 12.0f);
264 constexpr float zoomResponsiveness = 14.0f;
265 const float smoothing = 1.0f - std::exp(-zoomResponsiveness * deltaSeconds);
266 cameraDistance += (targetCameraDistance - cameraDistance) * smoothing;
269 [[nodiscard]]
const void *flippedMatrixTexturePixels(
int width,
int height,
int pitch) {
270 const auto *pixels =
static_cast<const Uint8 *
>(binaryMatrixTexture !=
nullptr ? binaryMatrixTexture->pixels() :
nullptr);
271 if (pixels ==
nullptr || width <= 0 || height <= 0 || pitch <= 0) {
275 const int rowBytes = width * 4;
276 flippedMatrixTexture.resize(
static_cast<std::size_t
>(rowBytes * height));
277 for (
int y = 0; y < height; ++y) {
278 const Uint8 *src = pixels +
static_cast<std::size_t
>(height - 1 - y) *
static_cast<std::size_t
>(pitch);
279 Uint8 *dst = flippedMatrixTexture.data() +
static_cast<std::size_t
>(y) *
static_cast<std::size_t
>(rowBytes);
280 std::memcpy(dst, src,
static_cast<std::size_t
>(rowBytes));
282 return flippedMatrixTexture.data();
285 void resetSkyboxCamera() {
286 skyboxCameraPosition = glm::vec3(0.0f);
287 skyboxYawDegrees = 180.0f;
288 skyboxPitchDegrees = 0.0f;
291 [[nodiscard]] glm::vec3 skyboxUpVector()
const {
292 const float yawRadians = glm::radians(skyboxYawDegrees);
293 const float upPitchRadians = glm::radians(skyboxPitchDegrees + 90.0f);
294 return glm::normalize(glm::vec3{std::sin(yawRadians) * std::cos(upPitchRadians), std::sin(upPitchRadians), -std::cos(yawRadians) * std::cos(upPitchRadians)});
297 [[nodiscard]] glm::vec3 skyboxForwardVector()
const {
298 const float yawRadians = glm::radians(skyboxYawDegrees);
299 const float pitchRadians = glm::radians(skyboxPitchDegrees);
300 return glm::normalize(glm::vec3{std::sin(yawRadians) * std::cos(pitchRadians), std::sin(pitchRadians), -std::cos(yawRadians) * std::cos(pitchRadians)});
303 [[nodiscard]] glm::vec3 skyboxRightVector()
const {
return glm::normalize(glm::cross(skyboxForwardVector(), skyboxUpVector())); }
305 void updateSkyboxCamera(
float deltaSeconds) {
306 const float lookSpeed = 85.0f;
307 if (skyboxLookLeftKey) {
308 skyboxYawDegrees -= lookSpeed * deltaSeconds;
310 if (skyboxLookRightKey) {
311 skyboxYawDegrees += lookSpeed * deltaSeconds;
313 if (skyboxLookUpKey) {
314 skyboxPitchDegrees += lookSpeed * deltaSeconds;
316 if (skyboxLookDownKey) {
317 skyboxPitchDegrees -= lookSpeed * deltaSeconds;
319 skyboxPitchDegrees = std::fmod(skyboxPitchDegrees, 360.0f);
320 if (skyboxPitchDegrees < 0.0f) {
321 skyboxPitchDegrees += 360.0f;
324 const float keyZoomDelta = (zoomInKey ? 1.0f : 0.0f) - (zoomOutKey ? 1.0f : 0.0f);
325 if (keyZoomDelta != 0.0f) {
326 constexpr float zoomSpeed = 2.25f;
327 skyboxCameraPosition += skyboxForwardVector() * (keyZoomDelta * zoomSpeed * deltaSeconds);
ModelWindow(const std::string filename, bool usingDefaultModel, const std::string &path, const std::string &resource, const std::string &resource_path, const std::string &fragmentShaderPath, const std::string &texture_path, const std::string &title, int width, int height, bool fullscreen, bool enable_vsync, bool binaryTextureMode, int fontSize, const std::string &fontPath, const std::string &color)