332 {
333 module.doc() = "Python bindings for the MXVK Vulkan framework.";
334
335 nb::exception<Exception>(module, "MXVKError", PyExc_RuntimeError);
336
337 const std::filesystem::path module_directory = python_module_directory();
338 const std::array<std::filesystem::path, 3> shader_directories{module_directory / "share" / "mxvk" / "shaders", module_directory / "mxvk" / "shaders", module_directory / "shaders"};
339 for (const std::filesystem::path &shader_directory : shader_directories) {
340 if (std::filesystem::is_directory(shader_directory)) {
342 break;
343 }
344 }
345
346 module.attr("version") = nb::make_tuple(MXVK_VERSION_CODE_MAJOR, MXVK_VERSION_CODE_MINOR, MXVK_VERSION_CODE_PATCH);
347 module.attr("has_cv") = false;
348 module.attr("has_ffmpeg_capture") = false;
349 module.attr("has_mixer") = false;
350 module.attr("has_jpeg") = false;
351 module.attr("EVENT_KEY_DOWN") = static_cast<uint32_t>(SDL_EVENT_KEY_DOWN);
352 module.attr("EVENT_KEY_UP") = static_cast<uint32_t>(SDL_EVENT_KEY_UP);
353 module.attr("EVENT_MOUSE_MOTION") = static_cast<uint32_t>(SDL_EVENT_MOUSE_MOTION);
354 module.attr("EVENT_MOUSE_BUTTON_DOWN") = static_cast<uint32_t>(SDL_EVENT_MOUSE_BUTTON_DOWN);
355 module.attr("EVENT_MOUSE_BUTTON_UP") = static_cast<uint32_t>(SDL_EVENT_MOUSE_BUTTON_UP);
356 module.attr("EVENT_MOUSE_WHEEL") = static_cast<uint32_t>(SDL_EVENT_MOUSE_WHEEL);
357 module.attr("KEY_ESCAPE") = static_cast<int32_t>(SDLK_ESCAPE);
358 module.attr("MOUSE_BUTTON_LEFT") = SDL_BUTTON_LEFT;
359 module.attr("MOUSE_BUTTON_MIDDLE") = SDL_BUTTON_MIDDLE;
360 module.attr("MOUSE_BUTTON_RIGHT") = SDL_BUTTON_RIGHT;
361 module.attr("MOUSE_BUTTON_X1") = SDL_BUTTON_X1;
362 module.attr("MOUSE_BUTTON_X2") = SDL_BUTTON_X2;
363 module.attr("MOD_SHIFT") = static_cast<uint16_t>(SDL_KMOD_SHIFT);
364 module.attr("MOD_CTRL") = static_cast<uint16_t>(SDL_KMOD_CTRL);
365 module.attr("MOD_ALT") = static_cast<uint16_t>(SDL_KMOD_ALT);
366 module.attr("MOD_GUI") = static_cast<uint16_t>(SDL_KMOD_GUI);
367
368 module.def("set_default_enable_screenshot", &setDefaultEnableScreenshot, nb::arg("enabled"));
369 module.def("default_enable_screenshot", &defaultEnableScreenshot);
370 module.def("set_default_executable_name", &setDefaultExecutableName, nb::arg("name"));
371 module.def("default_executable_name", &defaultExecutableName, nb::rv_policy::copy);
372 module.def("set_default_shader_directory", &setDefaultShaderDirectory, nb::arg("directory"));
373 module.def("default_shader_directory", &defaultShaderDirectory, nb::rv_policy::copy);
374 module.def(
375 "key_code",
376 [](const std::string &name) {
377 const SDL_Keycode key = SDL_GetKeyFromName(name.c_str());
378 if (key == SDLK_UNKNOWN)
379 throw std::invalid_argument("unknown SDL key name: " + name);
380 return static_cast<int32_t>(key);
381 },
382 nb::arg("name"));
383 module.def(
384 "key_name",
385 [](int32_t key) -> nb::object {
386 const char *name = SDL_GetKeyName(static_cast<SDL_Keycode>(key));
387 return name == nullptr || name[0] == '\0' ? nb::none() : nb::cast(std::string(name));
388 },
389 nb::arg("key"));
390 module.def("save_png_rgba", [](const std::string &path, nb::ndarray<uint8_t, nb::c_contig, nb::device::cpu> pixels, int width, int height) { return SavePNG_RGBA(path.c_str(), pixels.data(), width, height); }, nb::arg("path"), nb::arg("pixels"), nb::arg("width"), nb::arg("height"));
391 module.def("save_png_rgba16", [](const std::string &path, nb::ndarray<uint16_t, nb::c_contig, nb::device::cpu> pixels, int width, int height) { return SavePNG_RGBA16(path.c_str(), pixels.data(), width, height); }, nb::arg("path"), nb::arg("pixels"), nb::arg("width"), nb::arg("height"));
392 module.def("inspect_spirv_file", [](const std::string &path) { return inspect_spirv(load_spv(path)); }, nb::arg("path"));
393
396
399 module.def("create_uniform_buffer", [](VK_Window &window, size_t size) { return std::make_shared<PythonBuffer>(window.context(), size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); }, nb::arg("window"), nb::arg("size"), nb::keep_alive<0, 1>());
400 module.def("create_storage_buffer", [](VK_Window &window, size_t size) { return std::make_shared<PythonBuffer>(window.context(), size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); }, nb::arg("window"), nb::arg("size"), nb::keep_alive<0, 1>());
401 module.def("load_texture", [](VK_Window &window, const std::string &path) { return std::make_shared<PythonTexture>(window.context(), path); }, nb::arg("window"), nb::arg("path"), nb::keep_alive<0, 1>());
402
404
406
407#if defined(MXVK_WITH_JPEG)
408 module.attr("has_jpeg") = true;
409 module.def(
410 "load_jpeg_rgba",
411 [](const std::string &path) -> nb::object {
412 SDL_Surface *loaded = VK_JPEG::Load(path.c_str());
413 if (loaded == nullptr)
414 return nb::none();
415 SDL_Surface *rgba = SDL_ConvertSurface(loaded, SDL_PIXELFORMAT_RGBA32);
416 SDL_DestroySurface(loaded);
417 if (rgba == nullptr)
418 return nb::none();
419 const size_t byte_count = static_cast<size_t>(rgba->pitch) * static_cast<size_t>(rgba->h);
420 nb::bytes pixels(static_cast<const char *>(rgba->pixels), byte_count);
421 nb::tuple result = nb::make_tuple(pixels, rgba->w, rgba->h, rgba->pitch);
422 SDL_DestroySurface(rgba);
423 return result;
424 },
425 nb::arg("path"));
426 module.def(
427 "save_jpeg_rgba",
428 [](const std::string &path, nb::ndarray<uint8_t, nb::c_contig, nb::device::cpu> pixels, int width, int height, int quality, int pitch) {
429 if (width <= 0 || height <= 0)
430 throw nb::value_error("width and height must be positive");
431 if (pitch == 0)
432 pitch = width * 4;
433 if (pitch < width * 4 || pixels.nbytes() < static_cast<size_t>(pitch) * static_cast<size_t>(height))
434 throw nb::value_error("RGBA buffer is smaller than pitch * height");
435 SDL_Surface *surface = SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_RGBA32, pixels.data(), pitch);
436 if (surface == nullptr)
437 throw std::runtime_error(SDL_GetError());
438 const bool saved = VK_JPEG::SaveSurface(surface, path.c_str(), quality);
439 SDL_DestroySurface(surface);
440 return saved;
441 },
442 nb::arg("path"),
443 nb::arg("pixels"),
444 nb::arg("width"),
445 nb::arg("height"),
446 nb::arg("quality") = 90,
447 nb::arg("pitch") = 0);
448#endif
449
450#if defined(MXVK_WITH_MIXER)
451 module.attr("has_mixer") = true;
452 nb::class_<VK_Mixer>(module,
"Mixer").def(nb::init<>()).def(
"init", &
VK_Mixer::init).def(
"load_wav", &
VK_Mixer::loadWav, nb::arg(
"path")).def(
"load_music", &
VK_Mixer::loadMusic, nb::arg(
"path")).def(
"play_music", &
VK_Mixer::playMusic, nb::arg(
"id"), nb::arg(
"loops") = 0).def(
"play_wav", &
VK_Mixer::playWav, nb::arg(
"id"), nb::arg(
"loops") = 0, nb::arg(
"channel") = -1).def(
"is_playing", &
VK_Mixer::isPlaying, nb::arg(
"channel")).def(
"is_music_playing", &
VK_Mixer::isMusicPlaying, nb::arg(
"id")).def(
"stop_music", &
VK_Mixer::stopMusic).def(
"cleanup", &
VK_Mixer::cleanup);
453#endif
454
455#if defined(MXVK_WITH_CV)
456 module.attr("has_cv") = true;
457 nb::class_<VK_Capture>(module, "Capture")
458 .def(nb::init<>())
459 .def(
"open", nb::overload_cast<const std::string &>(&
VK_Capture::open), nb::arg(
"path"))
460 .def(
"open_camera", nb::overload_cast<int, int>(&
VK_Capture::open), nb::arg(
"index"), nb::arg(
"backend") = 0)
464 .def(
"draw", nb::overload_cast<int, int>(&
VK_Capture::draw), nb::arg(
"x"), nb::arg(
"y"))
465 .def(
"draw", nb::overload_cast<int, int, int, int>(&
VK_Capture::draw), nb::arg(
"x"), nb::arg(
"y"), nb::arg(
"width"), nb::arg(
"height"))
468 .def(
469 "read_rgba",
471 cv::Mat frame;
472 if (!capture.readRgba(frame, flip_y))
473 return nb::object(nb::none());
474 return opencv_rgba_array(std::move(frame));
475 },
476 nb::arg("flip_y") = false)
478 .def(
"read_to_sprite", nb::overload_cast<VK_Sprite &, bool>(&
VK_Capture::readToSprite), nb::arg(
"sprite"), nb::arg(
"flip_y"))
480 .def(
"reload", &
VK_Capture::reload, nb::arg(
"width"), nb::arg(
"height"), nb::arg(
"vertex_shader_path"), nb::arg(
"fragment_shader_path"))
484#if defined(MXVK_CUDA)
485 .def(
486 "read_gpu_rgba_to_host",
488 cv::cuda::GpuMat gpu;
489 if (!capture.readGpuRgba(gpu, flip_y))
490 return nb::object(nb::none());
491 cv::Mat host;
492 gpu.download(host, capture.cudaStream());
493 capture.cudaStream().waitForCompletion();
494 return opencv_rgba_array(std::move(host));
495 },
496 nb::arg("flip_y") = false)
497#endif
498 ;
499#endif
500
501#if defined(MXVK_WITH_FFMPEG_CAPTURE)
502 module.attr("has_ffmpeg_capture") = true;
503 nb::class_<VK_FF_Capture>(module, "FFmpegCapture")
504 .def(nb::init<>())
505 .def(
"open", nb::overload_cast<const std::string &>(&
VK_FF_Capture::open), nb::arg(
"path"))
506 .def(
"open", nb::overload_cast<const std::string &, int>(&
VK_FF_Capture::open), nb::arg(
"path"), nb::arg(
"cuda_device"))
511 .def(
512 "read_rgba",
514 std::vector<uint8_t> pixels;
515 int width = 0, height = 0, pitch = 0;
516 if (!capture.readRgba(pixels, width, height, pitch, flip_y))
517 return nb::none();
518 return ffmpeg_rgba8_array(std::move(pixels), width, height, pitch);
519 },
520 nb::arg("flip_y") = false)
521 .def(
522 "read_rgba16",
524 std::vector<uint16_t> pixels;
525 int width = 0, height = 0, pitch = 0;
526 if (!capture.readRgba16(pixels, width, height, pitch, flip_y))
527 return nb::none();
528 return ffmpeg_rgba16_array(std::move(pixels), width, height, pitch);
529 },
530 nb::arg("flip_y") = false)
536#if defined(MXVK_CUDA)
537 .def(
538 "read_gpu_rgba_to_host",
540 cv::cuda::GpuMat gpu;
541 cv::cuda::Stream stream;
542 if (!capture.readGpuRgba(gpu, stream, flip_y))
543 return nb::object(nb::none());
544 stream.waitForCompletion();
545 cv::Mat host;
546 gpu.download(host);
547 return opencv_rgba_array(std::move(host));
548 },
549 nb::arg("flip_y") = false)
550#endif
551 ;
552#endif
553
555
556 nb::class_<VK_Config>(module,
"Config").def(nb::init<>()).def(nb::init<const std::string &>(), nb::arg(
"file_path")).def(
"item_at_key", &
VK_Config::itemAtKey, nb::arg(
"section"), nb::arg(
"key"), nb::arg(
"default_value") =
"").def(
"set_item", &
VK_Config::setItem, nb::arg(
"section"), nb::arg(
"key"), nb::arg(
"value")).def(
"load_file", &
VK_Config::loadFile, nb::arg(
"path")).def(
"save_file", &
VK_Config::saveFile, nb::arg(
"path")).def(
"split_by_comma", &
VK_Config::splitByComma, nb::arg(
"value"));
557
558 nb::class_<VK_Joystick>(module,
"Joystick").def(nb::init<>()).def(
"open", &
VK_Joystick::open, nb::arg(
"index")).def(
"close", &
VK_Joystick::close).def(
"name", &
VK_Joystick::name).def(
"index", &
VK_Joystick::joystickIndex).def_static(
"count", &
VK_Joystick::joysticks).def(
"num_buttons", &
VK_Joystick::numButtons).def(
"num_hats", &
VK_Joystick::numHats).def(
"num_axes", &
VK_Joystick::numAxes).def(
"button", &
VK_Joystick::getButton, nb::arg(
"index")).def(
"hat", &
VK_Joystick::getHat, nb::arg(
"index")).def(
"axis", &
VK_Joystick::getAxis, nb::arg(
"index"));
559
560 nb::class_<VK_Controller>(module,
"Controller").def(nb::init<>()).def(
"open", &
VK_Controller::open, nb::arg(
"index")).def(
"close", &
VK_Controller::close).def(
"name", &
VK_Controller::name).def(
"index", &
VK_Controller::controllerIndex).def_static(
"count", &
VK_Controller::joysticks).def(
"button", [](
const VK_Controller &controller,
int button) {
return controller.getButton(
static_cast<SDL_GamepadButton
>(button)); }, nb::arg(
"button")).def(
"hat", &
VK_Controller::getHat, nb::arg(
"index")).def(
"axis", [](
const VK_Controller &controller,
int axis) {
return controller.getAxis(
static_cast<SDL_GamepadAxis
>(axis)); }, nb::arg(
"axis")).def(
"active", &
VK_Controller::active);
561
562 nb::class_<VKVertex>(module,
"Vertex").def(nb::init<>()).def_prop_rw(
"position", [](
const VKVertex &value) {
return nb::make_tuple(value.pos[0], value.pos[1], value.pos[2]); }, [](
VKVertex &value,
const std::array<float, 3> &position) { std::copy(position.begin(), position.end(), value.pos); }).def_prop_rw(
"tex_coord", [](
const VKVertex &value) {
return nb::make_tuple(value.texCoord[0], value.texCoord[1]); }, [](
VKVertex &value,
const std::array<float, 2> &tex_coord) { std::copy(tex_coord.begin(), tex_coord.end(), value.texCoord); }).def_prop_rw(
"normal", [](
const VKVertex &value) {
return nb::make_tuple(value.normal[0], value.normal[1], value.normal[2]); }, [](
VKVertex &value,
const std::array<float, 3> &normal) { std::copy(normal.begin(), normal.end(), value.normal); });
563
565
566 nb::class_<MXMaterial>(module,
"Material").def(nb::init<>()).def_rw(
"name", &
MXMaterial::name).def_prop_rw(
"ambient", [](
const MXMaterial &value) {
return nb::make_tuple(value.ka[0], value.ka[1], value.ka[2]); }, [](
MXMaterial &value,
const std::array<float, 3> &ambient) { std::copy(ambient.begin(), ambient.end(), value.ka); }).def_prop_rw(
"diffuse", [](
const MXMaterial &value) {
return nb::make_tuple(value.kd[0], value.kd[1], value.kd[2]); }, [](
MXMaterial &value,
const std::array<float, 3> &diffuse) { std::copy(diffuse.begin(), diffuse.end(), value.kd); }).def_prop_rw(
"specular", [](
const MXMaterial &value) {
return nb::make_tuple(value.ks[0], value.ks[1], value.ks[2]); }, [](
MXMaterial &value,
const std::array<float, 3> &specular) { std::copy(specular.begin(), specular.end(), value.ks); }).def_rw(
"shininess", &
MXMaterial::ns).def_rw(
"opacity", &
MXMaterial::d).def_rw(
"illumination_model", &
MXMaterial::illum).def_rw(
"diffuse_map", &
MXMaterial::map_kd);
567
568 nb::class_<MXModel>(module, "Model")
569 .def(nb::init<>())
570 .def(
"load", nb::overload_cast<const std::string &, float>(&
MXModel::load), nb::arg(
"path"), nb::arg(
"position_scale") = 1.0F)
571 .def(
"load", nb::overload_cast<const std::string &, const std::string &, const std::string &, float>(&
MXModel::load), nb::arg(
"path"), nb::arg(
"texture_manifest_path"), nb::arg(
"texture_base_path"), nb::arg(
"position_scale") = 1.0F)
572 .def(
"export_obj", nb::overload_cast<const std::string &, const std::string &>(&
MXModel::exportOBJ, nb::const_), nb::arg(
"obj_path"), nb::arg(
"mtl_path") =
"")
573 .def_static(
"export_obj_from_file", nb::overload_cast<const std::string &, const std::string &, const std::string &, const std::string &, float>(&
MXModel::exportOBJ), nb::arg(
"model_path"), nb::arg(
"texture_manifest_path"), nb::arg(
"texture_base_path"), nb::arg(
"obj_path"), nb::arg(
"position_scale") = 1.0F)
582
583 nb::class_<SDL_Color>(module, "Color").def(nb::init<>()).def(nb::init<uint8_t, uint8_t, uint8_t, uint8_t>(), nb::arg("red"), nb::arg("green"), nb::arg("blue"), nb::arg("alpha") = 255).def_rw("red", &SDL_Color::r).def_rw("green", &SDL_Color::g).def_rw("blue", &SDL_Color::b).def_rw("alpha", &SDL_Color::a);
584
585 nb::class_<VK_Console>(module, "Console")
586 .def(nb::init<>())
587 .def(
"attach", &
VK_Console::attach, nb::arg(
"window"), nb::arg(
"font_path"), nb::arg(
"font_size") = 18)
590 .def(
"print_line", &
VK_Console::printLine, nb::arg(
"line"), nb::arg(
"color") = SDL_Color{255, 255, 255, 255})
602
603 nb::class_<PointSpriteVertex>(module,
"PointSpriteVertex").def(nb::init<>()).def_prop_rw(
"position", [](
const PointSpriteVertex &value) {
return nb::make_tuple(value.position[0], value.position[1], value.position[2]); }, [](
PointSpriteVertex &value,
const std::array<float, 3> &position) { std::copy(position.begin(), position.end(), value.position); }).def_rw(
"size", &
PointSpriteVertex::size).def_prop_rw(
"color", [](
const PointSpriteVertex &value) {
return nb::make_tuple(value.color[0], value.color[1], value.color[2], value.color[3]); }, [](
PointSpriteVertex &value,
const std::array<float, 4> &color) { std::copy(color.begin(), color.end(), value.color); });
604
605 nb::class_<VK_PointSpriteBatch>(module, "PointSpriteBatch")
606 .def(nb::init<>())
607 .def(
"load", &
VK_PointSpriteBatch::load, nb::arg(
"window"), nb::arg(
"texture_path"), nb::arg(
"vertex_shader_path"), nb::arg(
"fragment_shader_path"), nb::arg(
"max_vertices"))
610 .def(
611 "upload_vertices",
612 [](
VK_PointSpriteBatch &batch,
const std::vector<PointSpriteVertex> &vertices) { batch.upload_vertices(vertices.data(), vertices.size()); },
613 nb::arg("vertices"))
620
621 nb::class_<BufferResource>(module,
"BufferResource").def(nb::init<>()).def_rw(
"size", &
BufferResource::size).def_prop_ro(
"buffer", [](
const BufferResource &value) {
return reinterpret_cast<uintptr_t
>(value.buffer); }).def_prop_ro(
"memory", [](
const BufferResource &value) {
return reinterpret_cast<uintptr_t
>(value.memory); }).def_prop_ro(
"mapped", [](
const BufferResource &value) {
return reinterpret_cast<uintptr_t
>(value.mapped); });
622
623 nb::class_<TextureResource>(module,
"TextureResource").def(nb::init<>()).def_rw(
"width", &
TextureResource::width).def_rw(
"height", &
TextureResource::height).def_prop_ro(
"image", [](
const TextureResource &value) {
return reinterpret_cast<uintptr_t
>(value.image); }).def_prop_ro(
"memory", [](
const TextureResource &value) {
return reinterpret_cast<uintptr_t
>(value.memory); }).def_prop_ro(
"view", [](
const TextureResource &value) {
return reinterpret_cast<uintptr_t
>(value.view); }).def_prop_ro(
"sampler", [](
const TextureResource &value) {
return reinterpret_cast<uintptr_t
>(value.sampler); });
624
626
627 nb::class_<VK_Stencil>(module, "Stencil")
628 .def(nb::init<>())
630 .def(
631 "resize",
632 [](
VK_Stencil &value, uint32_t width, uint32_t height) { value.resize(VkExtent2D{width, height}); },
633 nb::arg("width"),
634 nb::arg("height"))
636 .def_prop_ro(
"format", [](
const VK_Stencil &value) {
return static_cast<int>(value.stencil_format()); })
637 .def_prop_ro(
"extent", [](
const VK_Stencil &value) {
638 const auto extent = value.extent();
639 return nb::make_tuple(extent.width, extent.height);
640 });
641
642 nb::class_<Font>(module,
"Font").def(nb::init<>()).def(nb::init<const std::string &, int>(), nb::arg(
"path"), nb::arg(
"size")).def(
"reset", nb::overload_cast<>(&
Font::reset)).def(
"reset", nb::overload_cast<const std::string &, int>(&
Font::reset), nb::arg(
"path"), nb::arg(
"size")).def(
"valid", [](
const Font &font) {
return static_cast<bool>(font); }).def_prop_ro(
"path", &
Font::path).def_prop_ro(
"size", &
Font::size);
643
644 nb::class_<UniformBufferObject>(module,
"ModelUniforms").def(nb::init<>()).def_prop_rw(
"model", [](
const UniformBufferObject &value) {
return matrix_to_rows(value.model); }, [](
UniformBufferObject &value,
const std::array<float, 16> &matrix) { value.model =
matrix_from_rows(matrix); }).def_prop_rw(
"view", [](
const UniformBufferObject &value) {
return matrix_to_rows(value.view); }, [](
UniformBufferObject &value,
const std::array<float, 16> &matrix) { value.view =
matrix_from_rows(matrix); }).def_prop_rw(
"projection", [](
const UniformBufferObject &value) {
return matrix_to_rows(value.proj); }, [](
UniformBufferObject &value,
const std::array<float, 16> &matrix) { value.proj =
matrix_from_rows(matrix); }).def_prop_rw(
"effects", [](
const UniformBufferObject &value) {
return vector4_array(value.fx); }, [](
UniformBufferObject &value,
const std::array<float, 4> &effects) { value.fx =
vector4(effects); });
645
646 nb::class_<ModelFragmentPushConstants>(module,
"ModelFragmentPushConstants").def(nb::init<>()).def_rw(
"screen_width", &
ModelFragmentPushConstants::screenWidth).def_rw(
"screen_height", &
ModelFragmentPushConstants::screenHeight).def_rw(
"sprite_x", &
ModelFragmentPushConstants::spritePosX).def_rw(
"sprite_y", &
ModelFragmentPushConstants::spritePosY).def_rw(
"sprite_width", &
ModelFragmentPushConstants::spriteSizeW).def_rw(
"sprite_height", &
ModelFragmentPushConstants::spriteSizeH).def_rw(
"effects_enabled", &
ModelFragmentPushConstants::effectsOn).def_prop_rw(
"params", [](
const ModelFragmentPushConstants &value) {
return vector4_array(value.params); }, [](
ModelFragmentPushConstants &value,
const std::array<float, 4> ¶ms) { value.params =
vector4(params); });
647
648 nb::class_<ModelFragmentUniforms>(module, "ModelFragmentUniforms")
649 .def(nb::init<>())
650 .def_prop_rw(
651 "mouse",
654 .def_prop_rw(
655 "uniform0",
658 .def_prop_rw(
659 "uniform1",
662 .def_prop_rw(
663 "uniform2",
666 .def_prop_rw(
667 "uniform3",
670 .def_prop_rw(
671 "custom_uniforms",
673 std::vector<std::array<float, 4>> result;
674 result.reserve(value.custom_uniforms.size());
675 for (const auto &entry : value.custom_uniforms)
677 return result;
678 },
680 if (data.size() > value.custom_uniforms.size())
681 throw nb::value_error("at most 16 custom uniforms are supported");
682 value.custom_uniforms.fill(glm::vec4{0.0F});
683 for (size_t index = 0; index < data.size(); ++index)
684 value.custom_uniforms[index] =
vector4(data[index]);
685 })
686 .def_prop_rw(
687 "audio_bands",
691
692 nb::class_<VKAbstractModel>(module, "AbstractModel")
693 .def(nb::init<>())
694 .def(
"load", nb::overload_cast<VK_Window *, const std::string &, const std::string &, const std::string &, float>(&
VKAbstractModel::load), nb::arg(
"window"), nb::arg(
"model_path"), nb::arg(
"texture_manifest_path") =
"", nb::arg(
"texture_base_path") =
"", nb::arg(
"scale") = 1.0F, nb::keep_alive<1, 2>())
695 .def(
"set_shaders", &
VKAbstractModel::setShaders, nb::arg(
"window"), nb::arg(
"vertex_shader_path"), nb::arg(
"fragment_shader_path"))
700 .def(
701 "update_primary_texture",
702 [](
VKAbstractModel &model, nb::ndarray<uint8_t, nb::c_contig, nb::device::cpu> pixels,
int width,
int height,
int pitch) {
703 if (width <= 0 || height <= 0)
704 throw nb::value_error("width and height must be positive");
705 if (pitch == 0)
706 pitch = width * 4;
707 if (pitch < width * 4 || pixels.nbytes() < static_cast<size_t>(pitch) * static_cast<size_t>(height))
708 throw nb::value_error("RGBA buffer is smaller than pitch * height");
709 return model.updatePrimaryTexture(pixels.data(), width, height, pitch);
710 },
711 nb::arg("pixels"),
712 nb::arg("width"),
713 nb::arg("height"),
714 nb::arg("pitch") = 0)
715 .def(
716 "render",
717 [](
const VKAbstractModel &model, nb::capsule command_buffer, uint32_t image_index,
bool wireframe) {
718 auto command = static_cast<VkCommandBuffer>(command_buffer.data());
719 if (command == VK_NULL_HANDLE)
720 throw nb::value_error("command_buffer capsule must contain a VkCommandBuffer pointer");
721 model.render(command, image_index, wireframe);
722 },
723 nb::arg("command_buffer"),
724 nb::arg("image_index"),
725 nb::arg("wireframe") = false)
726 .def(
727 "render_with_push_constants",
729 auto command = static_cast<VkCommandBuffer>(command_buffer.data());
730 if (command == VK_NULL_HANDLE)
731 throw nb::value_error("command_buffer capsule must contain a VkCommandBuffer pointer");
732 model.renderWithPushConstants(command, image_index, texture_index, uniforms, wireframe);
733 },
734 nb::arg("command_buffer"),
735 nb::arg("image_index"),
736 nb::arg("texture_index"),
737 nb::arg("uniforms"),
738 nb::arg("wireframe") = false)
743 .def(
744 "set_color_attachment_format",
745 [](
VKAbstractModel &model,
int format) { model.setColorAttachmentFormat(
static_cast<VkFormat
>(format)); },
746 nb::arg("format"))
748 .def_prop_ro("center_offset",
750 const auto value = model.modelCenterOffset();
751 return nb::make_tuple(value.x, value.y, value.z);
752 })
754 .def_prop_ro("axis_extent",
756 const auto value = model.modelAxisExtent();
757 return nb::make_tuple(value.x, value.y, value.z);
758 })
760
761 auto sprite = nb::class_<VK_Sprite>(module, "Sprite");
762 sprite.def(
"load", nb::overload_cast<const std::string &, const std::string &>(&
VK_Sprite::loadSprite), nb::arg(
"path"), nb::arg(
"fragment_shader_path") =
"")
763 .def(
"create_empty", &
VK_Sprite::createEmptySprite, nb::arg(
"width"), nb::arg(
"height"), nb::arg(
"vertex_shader_path") =
"", nb::arg(
"fragment_shader_path") =
"")
764 .def(
"create_empty_rgba16", &
VK_Sprite::createEmptySpriteRgba16, nb::arg(
"width"), nb::arg(
"height"), nb::arg(
"vertex_shader_path") =
"", nb::arg(
"fragment_shader_path") =
"")
766 .def(
"draw", nb::overload_cast<int, int, float, float>(&
VK_Sprite::drawSprite), nb::arg(
"x"), nb::arg(
"y"), nb::arg(
"scale_x"), nb::arg(
"scale_y"))
767 .def(
"draw_rotated", nb::overload_cast<int, int, float, float, float>(&
VK_Sprite::drawSprite), nb::arg(
"x"), nb::arg(
"y"), nb::arg(
"scale_x"), nb::arg(
"scale_y"), nb::arg(
"rotation"))
769 .def(
770 "update_texture",
771 [](
VK_Sprite &value, nb::ndarray<uint8_t, nb::c_contig, nb::device::cpu> pixels,
int width,
int height,
int pitch) { value.updateTexture(pixels.data(), width, height, pitch); },
772 nb::arg("pixels"),
773 nb::arg("width"),
774 nb::arg("height"),
775 nb::arg("pitch") = 0)
776 .def(
777 "update_texture_rgba16",
778 [](
VK_Sprite &value, nb::ndarray<uint16_t, nb::c_contig, nb::device::cpu> pixels,
int width,
int height,
int pitch) { value.updateTextureRgba16(pixels.data(), width, height, pitch); },
779 nb::arg("pixels"),
780 nb::arg("width"),
781 nb::arg("height"),
782 nb::arg("pitch") = 0)
783 .def(
"set_shader_params", &
VK_Sprite::setShaderParams, nb::arg(
"p1") = 0.0F, nb::arg(
"p2") = 0.0F, nb::arg(
"p3") = 0.0F, nb::arg(
"p4") = 0.0F)
789 .def(
"enable_compute_shader", &
VK_Sprite::enableComputeShader, nb::arg(
"path"), nb::arg(
"local_size_x"), nb::arg(
"local_size_y"), nb::arg(
"local_size_z") = 1)
790 .def(
"enable_instancing", &
VK_Sprite::enableInstancing, nb::arg(
"max_instances"), nb::arg(
"vertex_shader_path"), nb::arg(
"fragment_shader_path"))
794 .def(
"set_mouse_state", &
VK_Sprite::setMouseState, nb::arg(
"x"), nb::arg(
"y"), nb::arg(
"pressed"), nb::arg(
"reserved") = 0.0F)
799 .def(
"set_audio_bands", &
VK_Sprite::setAudioBands, nb::arg(
"low"), nb::arg(
"mid"), nb::arg(
"high"), nb::arg(
"reserved") = 0.0F)
809
810 auto sprite3d = nb::class_<VK_Sprite3D>(module, "Sprite3D");
811 sprite3d.def(nb::init<>())
812 .def(
"load", nb::overload_cast<VK_Window *, const std::string &, const std::string &, const std::string &>(&
VK_Sprite3D::load), nb::arg(
"window"), nb::arg(
"path"), nb::arg(
"vertex_shader_path") =
"", nb::arg(
"fragment_shader_path") =
"")
813 .def(
814 "update_camera",
815 [](
VK_Sprite3D &value, uint32_t image_index,
const std::array<float, 16> &view,
const std::array<float, 16> &projection) { value.updateCamera(image_index,
matrix_from_rows(view),
matrix_from_rows(projection)); },
816 nb::arg("image_index"),
817 nb::arg("view"),
818 nb::arg("projection"))
819 .def(
820 "draw",
821 [](
VK_Sprite3D &value,
const std::array<float, 3> &position,
const std::array<float, 2> &size,
const std::array<float, 4> &color,
float rotation) { value.drawSprite(glm::vec3{position[0], position[1], position[2]}, glm::vec2{size[0], size[1]},
vector4(color), rotation); },
822 nb::arg("position"),
823 nb::arg("size"),
824 nb::arg("color") = std::array<float, 4>{1.0F, 1.0F, 1.0F, 1.0F},
825 nb::arg("rotation") = 0.0F)
826 .def(
827 "render",
828 [](
VK_Sprite3D &value, nb::capsule command_buffer, uint32_t image_index) {
829 auto command = static_cast<VkCommandBuffer>(command_buffer.data());
830 if (command == VK_NULL_HANDLE)
831 throw nb::value_error("command_buffer capsule must contain a VkCommandBuffer pointer");
832 value.render(command, image_index);
833 },
834 nb::arg("command_buffer"),
835 nb::arg("image_index"))
845
847
849
851
852 nb::class_<VulkanContext>(module,
"VulkanContext").def_prop_ro(
"device", [](
const VulkanContext &context) {
return reinterpret_cast<uintptr_t
>(context.device); }).def_prop_ro(
"physical_device", [](
const VulkanContext &context) {
return reinterpret_cast<uintptr_t
>(context.physical_device); }).def_prop_ro(
"graphics_queue", [](
const VulkanContext &context) {
return reinterpret_cast<uintptr_t
>(context.graphics_queue); }).def_prop_ro(
"command_pool", [](
const VulkanContext &context) {
return reinterpret_cast<uintptr_t
>(context.command_pool); });
853
855
856 nb::class_<VK_Window, PythonWindow>(module, "Window")
857 .def(nb::init<>())
858 .def(nb::init<const std::string &, int, int, bool, bool, VK_Window::PresentModePreference, VK_Window::RuntimeMode>(), nb::arg(
"title"), nb::arg(
"width"), nb::arg(
"height"), nb::arg(
"fullscreen") =
false, nb::arg(
"validation") =
true, nb::arg(
"present_mode") =
VK_Window::PresentModePreference::LowLatency, nb::arg(
"runtime_mode") =
VK_Window::RuntimeMode::Windowed)
859 .def(nb::init<const std::string &, int, int, bool, bool, bool>(), nb::arg("title"), nb::arg("width"), nb::arg("height"), nb::arg("fullscreen"), nb::arg("validation"), nb::arg("enable_vsync"))
861 .def(
"wait_idle", [](
VK_Window &window) {
862 const VkDevice device = window.getDevice();
863 if (device == VK_NULL_HANDLE)
864 return;
865
866 const VkResult result = vkDeviceWaitIdle(device);
867 if (result != VK_SUCCESS)
868 throw std::runtime_error("vkDeviceWaitIdle failed");
869 })
870 .def(
"request_exit", [](
VK_Window &window) {
871 auto *python_window = dynamic_cast<PythonWindow *>(&window);
872 if (python_window == nullptr)
873 throw nb::type_error("request_exit requires a Python subclass of Window");
874 python_window->request_exit();
875 })
877 .def(
878 "event",
879 [](
VK_Window &window, nb::capsule event) {
880 auto *sdl_event = static_cast<SDL_Event *>(event.data());
881 if (sdl_event == nullptr)
882 throw nb::value_error("event capsule must contain an SDL_Event pointer");
883 window.event(*sdl_event);
884 },
885 nb::arg("event"))
888 .def(
"on_swapchain_recreated", [](
VK_Window &) {})
889 .def(
"on_frame_readback_scheduled", [](
VK_Window &) {})
890 .def(
"on_frame_readback", [](
VK_Window &, nb::ndarray<nb::numpy, std::uint8_t>, uint32_t, uint32_t) {}, nb::arg(
"pixels"), nb::arg(
"width"), nb::arg(
"height"))
891 .def(
892 "flush_frame_readbacks",
894 auto *python_window = dynamic_cast<PythonWindow *>(&window);
895 if (python_window == nullptr)
896 throw nb::type_error("flush_frame_readbacks requires a Python subclass of Window");
897 python_window->flush_frame_readbacks();
898 })
899 .def(
"on_record_custom_rendering", [](
VK_Window &, nb::capsule, uint32_t) {}, nb::arg(
"command_buffer"), nb::arg(
"image_index"))
900 .def(
901 "on_prepare_frame_rendering",
902 [](
VK_Window &window, nb::capsule command_buffer, uint32_t image_index) {
903 auto command_buffer_handle = static_cast<VkCommandBuffer>(command_buffer.data());
904 if (command_buffer_handle == VK_NULL_HANDLE)
905 throw nb::value_error("command_buffer capsule must contain a VkCommandBuffer pointer");
906 window.onPrepareFrameRendering(command_buffer_handle, image_index);
907 },
908 nb::arg("command_buffer"),
909 nb::arg("image_index"))
914 .def(
"print_text", nb::overload_cast<const std::string &, int, int, const SDL_Color &>(&
VK_Window::printText), nb::arg(
"text"), nb::arg(
"x"), nb::arg(
"y"), nb::arg(
"color"))
915 .def(
"print_text", nb::overload_cast<const std::string &, int, int, const SDL_Color &, const Font &>(&
VK_Window::printText), nb::arg(
"text"), nb::arg(
"x"), nb::arg(
"y"), nb::arg(
"color"), nb::arg(
"font"))
916 .def(
"print_preview_text", nb::overload_cast<const std::string &, int, int, const SDL_Color &>(&
VK_Window::printPreviewText), nb::arg(
"text"), nb::arg(
"x"), nb::arg(
"y"), nb::arg(
"color"))
917 .def(
"print_preview_text", nb::overload_cast<const std::string &, int, int, const SDL_Color &, const Font &>(&
VK_Window::printPreviewText), nb::arg(
"text"), nb::arg(
"x"), nb::arg(
"y"), nb::arg(
"color"), nb::arg(
"font"))
919 .def(
"set_clear_color", &
VK_Window::setClearColor, nb::arg(
"red"), nb::arg(
"green"), nb::arg(
"blue"), nb::arg(
"alpha") = 1.0F)
929 .def(
930 "get_text_dimensions",
931 [](
VK_Window &window,
const std::string &text) -> nb::object {
932 int width = 0;
933 int height = 0;
934 if (!window.getTextDimensions(text, width, height))
935 return nb::none();
936 return nb::cast(nb::make_tuple(width, height));
937 },
938 nb::arg("text"))
939 .def(
940 "get_text_dimensions",
941 [](
VK_Window &window,
const std::string &text,
const Font &font) -> nb::object {
942 int width = 0;
943 int height = 0;
944 if (!window.getTextDimensions(text, width, height, font))
945 return nb::none();
946 return nb::cast(nb::make_tuple(width, height));
947 },
948 nb::arg("text"),
949 nb::arg("font"))
950 .def(
951 "create_sprite",
952 [](
VK_Window &window,
const std::string &path,
const std::string &vertex_shader_path,
const std::string &fragment_shader_path) {
return window.createSprite(path, vertex_shader_path, fragment_shader_path); },
953 nb::arg("path"),
954 nb::arg("vertex_shader_path") = "",
955 nb::arg("fragment_shader_path") = "",
956 nb::rv_policy::reference_internal)
957 .def(
958 "create_sprite_from_surface",
959 [](
VK_Window &window, nb::capsule surface,
const std::string &vertex_shader_path,
const std::string &fragment_shader_path) {
960 auto *sdl_surface = static_cast<SDL_Surface *>(surface.data());
961 if (sdl_surface == nullptr)
962 throw nb::value_error("surface capsule must contain an SDL_Surface pointer");
963 return window.createSprite(sdl_surface, vertex_shader_path, fragment_shader_path);
964 },
965 nb::arg("surface"),
966 nb::arg("vertex_shader_path") = "",
967 nb::arg("fragment_shader_path") = "",
968 nb::rv_policy::reference_internal)
969 .def(
970 "create_sprite",
971 [](
VK_Window &window,
int width,
int height,
const std::string &vertex_shader_path,
const std::string &fragment_shader_path, uint32_t spectrum_bin_count) {
return window.createSprite(width, height, vertex_shader_path, fragment_shader_path, spectrum_bin_count); },
972 nb::arg("width"),
973 nb::arg("height"),
974 nb::arg("vertex_shader_path") = "",
975 nb::arg("fragment_shader_path") = "",
976 nb::arg("spectrum_bin_count") = 0,
977 nb::rv_policy::reference_internal)
978 .def(
979 "create_sprite_with_spectrum_history",
980 [](
VK_Window &window,
int width,
int height,
const std::string &vertex_shader_path,
const std::string &fragment_shader_path, uint32_t spectrum_bin_count, uint32_t spectrum_history_layer_count) {
return window.createSprite(width, height, vertex_shader_path, fragment_shader_path, spectrum_bin_count, spectrum_history_layer_count); },
981 nb::arg("width"),
982 nb::arg("height"),
983 nb::arg("vertex_shader_path"),
984 nb::arg("fragment_shader_path"),
985 nb::arg("spectrum_bin_count"),
986 nb::arg("spectrum_history_layer_count"),
987 nb::rv_policy::reference_internal)
988 .def(
"attach_post_processing_shader", &
VK_Window::attachPostProcessingShader, nb::arg(
"fragment_shader_path"), nb::arg(
"p1") = 0.0F, nb::arg(
"p2") = 0.0F, nb::arg(
"p3") = 0.0F, nb::arg(
"p4") = 0.0F, nb::rv_policy::reference_internal)
991 .def(
"set_post_processing_shader_params", nb::overload_cast<float, float, float, float>(&
VK_Window::setPostProcessingShaderParams), nb::arg(
"p1") = 0.0F, nb::arg(
"p2") = 0.0F, nb::arg(
"p3") = 0.0F, nb::arg(
"p4") = 0.0F)
992 .def(
"set_post_processing_effect_params", nb::overload_cast<size_t, float, float, float, float>(&
VK_Window::setPostProcessingShaderParams), nb::arg(
"effect_index"), nb::arg(
"p1") = 0.0F, nb::arg(
"p2") = 0.0F, nb::arg(
"p3") = 0.0F, nb::arg(
"p4") = 0.0F)
999 .def(
1000 "create_sprite3d",
1001 [](
VK_Window &window,
const std::string &path,
const std::string &vertex_shader_path,
const std::string &fragment_shader_path) {
return window.createSprite3D(path, vertex_shader_path, fragment_shader_path); },
1002 nb::arg("path"),
1003 nb::arg("vertex_shader_path") = "",
1004 nb::arg("fragment_shader_path") = "",
1005 nb::rv_policy::reference_internal)
1006 .def(
1007 "create_sprite3d_from_surface",
1008 [](
VK_Window &window, nb::capsule surface,
const std::string &vertex_shader_path,
const std::string &fragment_shader_path) {
1009 auto *sdl_surface = static_cast<SDL_Surface *>(surface.data());
1010 if (sdl_surface == nullptr)
1011 throw nb::value_error("surface capsule must contain an SDL_Surface pointer");
1012 return window.createSprite3D(sdl_surface, vertex_shader_path, fragment_shader_path);
1013 },
1014 nb::arg("surface"),
1015 nb::arg("vertex_shader_path") = "",
1016 nb::arg("fragment_shader_path") = "",
1017 nb::rv_policy::reference_internal)
1021 .def_prop_ro(
"sdl_window", [](
const VK_Window &window) {
return reinterpret_cast<uintptr_t
>(window.getSDLWindow()); })
1022 .def_prop_ro(
"device", [](
const VK_Window &window) {
return reinterpret_cast<uintptr_t
>(window.getDevice()); })
1023 .def_prop_ro(
"physical_device", [](
const VK_Window &window) {
return reinterpret_cast<uintptr_t
>(window.getPhysicalDevice()); })
1024 .def_prop_ro(
"graphics_queue", [](
const VK_Window &window) {
return reinterpret_cast<uintptr_t
>(window.getGraphicsQueue()); })
1025 .def_prop_ro(
"command_pool", [](
const VK_Window &window) {
return reinterpret_cast<uintptr_t
>(window.getCommandPool()); })
1026 .def_prop_ro(
"pipeline_cache", [](
const VK_Window &window) {
return reinterpret_cast<uintptr_t
>(window.getPipelineCache()); })
1027 .def_prop_ro(
"swapchain_format", [](
const VK_Window &window) {
return static_cast<int>(window.getSwapchainFormat()); })
1028 .def_prop_ro(
"scene_color_format", [](
const VK_Window &window) {
return static_cast<int>(window.getSceneColorFormat()); })
1029 .def_prop_ro(
"depth_format", [](
const VK_Window &window) {
return static_cast<int>(window.getDepthFormat()); })
1030 .def_prop_ro("swapchain_extent",
1032 const VkExtent2D extent = window.getSwapchainExtent();
1033 return nb::make_tuple(extent.width, extent.height);
1034 })
1035 .def_prop_ro("render_extent",
1037 const VkExtent2D extent = window.getRenderExtent();
1038 return nb::make_tuple(extent.width, extent.height);
1039 })
1041
1042 nb::class_<PythonEvent>(module,
"Event").def_prop_ro(
"type", &
PythonEvent::type).def_prop_ro(
"timestamp", &
PythonEvent::timestamp).def_prop_ro(
"key", &
PythonEvent::key).def_prop_ro(
"scancode", &
PythonEvent::scancode).def_prop_ro(
"modifiers", &
PythonEvent::modifiers).def_prop_ro(
"key_down", &
PythonEvent::key_down).def_prop_ro(
"key_up", &
PythonEvent::key_up).def_prop_ro(
"down", &
PythonEvent::down).def_prop_ro(
"repeat", &
PythonEvent::repeat).def_prop_ro(
"text", &
PythonEvent::text).def_prop_ro(
"mouse_motion", &
PythonEvent::mouse_motion).def_prop_ro(
"mouse_button_down", &
PythonEvent::mouse_button_down).def_prop_ro(
"mouse_button_up", &
PythonEvent::mouse_button_up).def_prop_ro(
"mouse_wheel", &
PythonEvent::mouse_wheel).def_prop_ro(
"x", &
PythonEvent::x).def_prop_ro(
"y", &
PythonEvent::y).def_prop_ro(
"relative_x", &
PythonEvent::relative_x).def_prop_ro(
"relative_y", &
PythonEvent::relative_y).def_prop_ro(
"button", &
PythonEvent::button).def_prop_ro(
"clicks", &
PythonEvent::clicks).def_prop_ro(
"wheel_x", &
PythonEvent::wheel_x).def_prop_ro(
"wheel_y", &
PythonEvent::wheel_y).def_prop_ro(
"wheel_ticks_x", &
PythonEvent::wheel_ticks_x).def_prop_ro(
"wheel_ticks_y", &
PythonEvent::wheel_ticks_y).def_prop_ro(
"wheel_flipped", &
PythonEvent::wheel_flipped);
1043
1044 nb::class_<VK_IOWindow, VK_Window, PythonIOWindow>(module, "IOWindow")
1045 .def(nb::init<const std::string &, const std::string &, int, int, bool, bool>(), nb::arg("path"), nb::arg("title"), nb::arg("width"), nb::arg("height"), nb::arg("fullscreen") = false, nb::arg("enable_vsync") = false)
1046 .def(
"print", &
VK_IOWindow::print, nb::arg(
"text"), nb::arg(
"color") = SDL_Color{255, 255, 255, 255})
1049 .def(
1050 "console_event",
1052 auto *value = static_cast<SDL_Event *>(event.data());
1053 if (value == nullptr)
1054 throw nb::value_error("event capsule must contain an SDL_Event pointer");
1055 window.console_event(*value);
1056 },
1057 nb::arg("event"))
1059
1060 module.attr("VK_Window") = module.attr("Window");
1061 module.attr("VK_IOWindow") = module.attr("IOWindow");
1062 }
void Stop()
Stop the current interval and print its duration.
void Echo(std::string_view name) const
Print the interval recorded by the clock policy.
unsigned long TimePassed() const
Query the time elapsed since the most recent Start().
void Start(std::string_view name)
Start a new timed interval.
void write(nanobind::ndarray< uint8_t, nanobind::c_contig, nanobind::device::cpu > bytes)
int32_t wheel_ticks_y() const
uint64_t timestamp() const
int32_t wheel_ticks_x() const
const std::string & text() const
bool wheel_flipped() const
bool mouse_button_up() const
uint32_t scancode() const
bool mouse_motion() const
bool mouse_button_down() const
uint16_t modifiers() const
Small RAII wrapper for an SDL_ttf font handle.
int size() const noexcept
const std::string & path() const noexcept
const std::vector< MXMaterial > & materials() const
uint32_t indexCount() const
const std::vector< VKVertex > & vertices() const
const std::string & mtlLibPath() const
void compressIndices()
Remove duplicate vertices and remap indices.
size_t subMeshCount() const
const std::vector< uint32_t > & indices() const
void load(const std::string &path, float positionScale=1.0f)
Parse model data from disk into CPU-side arrays.
const std::vector< SubMesh > & subMeshes() const
void exportOBJ(const std::string &objPath, const std::string &mtlPath="") const
Export the loaded model as Wavefront OBJ plus MTL.
Convenience wrapper that owns mesh, textures, descriptors, and pipeline state.
void setAlphaBlending(bool enabled)
Enable or disable alpha blending for this model pipeline.
void setBackfaceCulling(bool enabled)
Enable or disable backface culling for this model pipeline.
void updateFragmentUBO(uint32_t imageIndex, const ModelFragmentUniforms &uniforms)
Update extended fragment uniforms for one swapchain image.
void updateUBO(uint32_t imageIndex, const UniformBufferObject &ubo)
Update one per-frame UBO payload.
float modelRenderScale() const
Access the computed render scale used for normalization.
void load(VK_Window *window, const std::string &modelPath, const std::string &textureManifestPath, const std::string &textureBasePath, float scale=1.0f)
Load mesh/texture resources and build Vulkan state.
void setShaders(VK_Window *window, const std::string &vertSpv, const std::string &fragSpv)
Configure custom shader paths and rebuild pipelines.
bool isLoaded() const
True once the model has been uploaded to GPU buffers.
void cleanup(VK_Window *window)
Destroy all owned Vulkan resources.
const MXModel & model() const
Access the underlying mesh object.
void resize(VK_Window *window)
Rebuild swapchain-dependent resources after resize.
void enableExtendedFragmentUniforms()
Use binding 1 for fragment uniforms and binding 2 for model transforms.
void setFragmentPushConstants(const ModelFragmentPushConstants &constants)
Set sprite-compatible push constants used by custom fragment shaders.
Vulkan OpenCV video capture source.
bool readToModelTexture(VKAbstractModel &model, bool flipY=false)
bool open(const std::string &filename)
Open a video file.
bool reload(size_t width, size_t height, const std::string &vert, const std::string &frag)
Replace the vertex/fragment shaders.
void resetSprite()
Destroy the current sprite and its Vulkan resources.
bool grab()
Decode and discard the next capture frame without conversion.
VK_Sprite * getSprite()
Access the backing sprite.
void close()
Close the capture device.
double get(unsigned int option)
Query a VideoCapture property.
bool readToSprite(VK_Sprite &targetSprite)
void draw(int x, int y, int width, int height)
Draw the current frame at a specified position and size.
bool is_open() const
Check whether the capture device is open.
bool read()
Capture and upload the next video frame.
void set(unsigned int option, double value)
Set a VideoCapture property.
std::string value
Value associated with the key.
std::string key
Configuration key name.
std::vector< std::string > splitByComma(const std::string &str) const
Split a string on comma delimiters.
void saveFile(const std::string &f2)
Save the current configuration to a file.
void loadFile(const std::string &f)
Load (or reload) a configuration file from disk.
VK_ConfigItem itemAtKey(const std::string §ion, const std::string &key, const std::string &default_value="") const
Retrieve a configuration item by section and key.
void setItem(const std::string §ion, const std::string &key, const std::string &value)
Insert or update a configuration item.
void setVisible(bool value) noexcept
Show or hide the console.
void draw()
Draw the current console overlay.
void setSpriteYOriginTopLeft(bool enabled) noexcept
Select whether console sprite elements use top-left Y coordinates.
const std::string & inputBuffer() const noexcept
void toggle() noexcept
Toggle console visibility.
bool isVisible() const noexcept
void hide() noexcept
Hide console.
void setFont(const std::string &fontPath, int fontSize)
Change only the console font, leaving the window default font unchanged.
void setPrompt(const std::string &prompt)
Set the input prompt text.
void show() noexcept
Show console.
void clear()
Clear all buffered output lines.
void setMaxVisibleLines(std::size_t maxVisibleLines)
Set max number of lines rendered on screen.
void printLine(const std::string &line, SDL_Color color=SDL_Color{255, 255, 255, 255})
Append one line to console output.
void attach(VK_Window &window, const std::string &fontPath, int fontSize=18)
Attach this console to a window and configure console font rendering.
void setMaxLines(std::size_t maxLines)
Set max number of retained output lines.
RAII wrapper for SDL_Gamepad (standard layout mapping).
int controllerIndex() const
Return the device index this controller was opened with.
void close()
Close the controller handle if open.
bool active() const
Check whether the controller is currently usable.
Uint8 getHat(int hat) const
Read the position of a POV hat.
static int joysticks()
Return the number of connected joysticks/controllers.
bool open(int index)
Open a gamepad by device index.
std::string name() const
Return the controller's name as reported by SDL.
FFmpeg-backed video-file capture source.
void close()
Close the active file and release decoder resources.
bool is_open() const
Check whether a decoder is open.
double fps() const
Source frame rate, falling back to 30 fps when unknown.
int width() const
Source width in pixels.
int height() const
Source height in pixels.
bool open(const std::string &filename)
Open a video file.
int hardware_decode_device() const
Selected CUDA hardware-decode device, or -1 for FFmpeg's default.
bool skip()
Decode and discard the next video frame.
bool using_hardware_decode() const
True when CUDA hardware decode is active.
bool seek_start()
Seek the active video stream back to its beginning.
virtual void console_proc()=0
void print(const std::string &text, SDL_Color col={255, 255, 255, 255})
bool getButton(int button) const
Test whether a button is currently pressed.
bool open(int index)
Open the joystick at the given device index.
Uint8 getHat(int hat) const
Read the position of a POV hat.
static int joysticks()
Return the number of connected joysticks.
int joystickIndex() const
Return the device index this joystick was opened with.
std::string name() const
Return the joystick's name as reported by SDL.
Sint16 getAxis(int axis) const
Read the current axis value.
void close()
Close the joystick handle if open.
int loadWav(const std::string &filename)
Load a WAV file as a sound effect chunk.
int playWav(int id, int value=0, int channel=-1)
Play a previously loaded WAV chunk.
void stopMusic()
Stop background music playback immediately.
void cleanup()
Free all loaded audio chunks and music tracks.
bool isPlaying(int channel) const
Query whether a sound track is currently playing.
void init()
Open the audio device (called automatically by constructor).
int loadMusic(const std::string &filename)
Load an audio file as streaming background music.
bool isMusicPlaying(int id) const
Query whether a loaded music track is currently playing.
int playMusic(int id, int value=0)
Start playing a previously loaded music track.
Reusable Vulkan point-list renderer for textured particle effects.
size_t vertex_count() const
void load(VK_Window *window, const std::string &texture_path, const std::string &vertex_shader_path, const std::string &fragment_shader_path, size_t max_vertices)
Create texture, vertex buffer, descriptors, and point-list pipeline.
void set_depth_write_enabled(bool enabled)
Enable or disable depth writes in the point-sprite pipeline.
void resize(VK_Window *window)
Recreate swapchain-dependent resources after a window resize.
void cleanup()
Destroy all owned resources and reset the batch to an unloaded state.
void set_additive_blending(bool enabled)
Select additive or alpha-over color blending.
void set_depth_test_enabled(bool enabled)
Enable or disable depth testing in the point-sprite pipeline.
Depth-tested 3D billboard sprite batch.
void cleanup()
Destroy all owned Vulkan resources.
void setDepthWriteEnabled(bool enabled)
Enable or disable depth writes for the 3D sprite pipeline.
void setDepthTestEnabled(bool enabled)
Enable or disable depth testing for the 3D sprite pipeline.
void load(VK_Window *window, const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Load sprite texture and build the 3D billboard pipeline from a PNG file.
void clearQueue()
Discard all queued sprite draws without rendering them.
void resize(VK_Window *window)
Rebuild swapchain-dependent resources after resize.
void setAlphaDiscardThreshold(float threshold)
Set the alpha threshold used to discard transparent texels.
void createEmptySpriteRgba16(int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a blank RGBA16 UNORM sprite texture.
void setUniform3(float x, float y, float z, float w)
Upload user uniform 3 to the extended UBO.
void enableInstancing(uint32_t maxInstances, const std::string &instanceVertShaderPath, const std::string &instanceFragShaderPath)
Enable GPU instancing for this sprite type.
void setVertexShaderPath(const std::string &path)
Override the vertex shader path (used when rebuilding the pipeline).
bool isInstancingEnabled() const
void setUniform1(float x, float y, float z, float w)
Upload user uniform 1 to the extended UBO.
bool getEffectsEnabled() const
void setUniform0(float x, float y, float z, float w)
Upload user uniform 0 to the extended UBO.
void shareHistoryTexture(const VK_Sprite &source)
Bind another sprite's history array without taking ownership.
void enableComputeShader(const std::string &path, uint32_t localSizeX, uint32_t localSizeY, uint32_t localSizeZ=1)
Build a compute image-effect pipeline for this sprite.
void setShaderParams(float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
Set up to four custom shader float parameters.
bool isExtendedUBOEnabled() const
void enableExtendedUBO()
Allocate and initialise the extended uniform buffer object.
bool hasOwnPipeline() const
void loadSprite(const std::string &pngPath, const std::string &fragmentShaderPath="")
Load sprite texture from a PNG file.
void createEmptySprite(int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a blank (un-initialised) sprite texture.
uint32_t enableSpectrumHistoryTexture(uint32_t bins, uint32_t layers)
Allocate a shader-readable FFT spectrum-history array.
void drawSprite(int x, int y)
Queue a draw at the given pixel position.
void setMouseState(float mx, float my, float pressed, float reserved=0.0f)
Upload mouse state to the extended UBO.
void setEffectsEnabled(bool enabled)
Enable or disable the custom fragment shader effects.
void enableHistoryTexture(uint32_t width, uint32_t height, uint32_t layers)
Allocate a shader-readable RGBA history texture array.
void setCustomUniforms(const std::vector< float > &values)
Upload ordered custom float values to the extended UBO.
void setFragmentShaderPath(const std::string &path)
Replace the fragment shader path and rebuild the custom pipeline.
void clearQueue()
Discard all pending draw commands without rendering.
void enableHistoryTextureRgba16Float(uint32_t width, uint32_t height, uint32_t layers)
Allocate a shader-readable RGBA16F history texture array.
void enableSpectrumTexture(uint32_t bins)
Allocate a shader-readable 1-D floating-point spectrum texture.
void setAudioBands(float low, float mid, float high, float reserved=0.0f)
Upload audio frequency-band energy to the extended UBO.
void setUniform2(float x, float y, float z, float w)
Upload user uniform 2 to the extended UBO.
void drawSpriteRect(int x, int y, int w, int h)
Queue a draw into an explicit destination rectangle.
Owns a stencil attachment and two fullscreen pipelines for stencil-masked rendering.
void destroy()
Release all Vulkan resources and reset the helper to an empty state.
bool valid() const noexcept
Check whether image resources are ready for rendering.
Main Vulkan window wrapper for MXVK.
bool screenshotEnabled() const noexcept
Check whether F10 screenshot capture is enabled.
void loop()
Run the main event/render loop.
virtual bool initVulkan(bool validiation)
Initialize Vulkan state.
VulkanContext context() const
void setFrameReadbackRgba16Enabled(bool enabled) noexcept
Request normalized RGBA16 callbacks from the final HDR intermediate.
void enablePostProcessing(VK_Sprite *sprite)
void setRenderExtent(uint32_t width, uint32_t height)
Render post-processing at a fixed extent independent of the preview window.
void clearTextQueue()
Clear all queued text draw calls for the current frame.
size_t getSwapchainImageCount() const noexcept
Get the number of swapchain images currently allocated.
void saveSnapshot(const std::string &path)
Save the most recently rendered window contents as a PNG file.
void setClearColor(float r, float g, float b, float a=1.0f)
Set the per-frame color attachment clear color.
std::vector< VK_Sprite * > attachPostProcessingShaders(const std::vector< PostProcessingEffect > &effects)
void setHdrRenderIntermediatesEnabled(bool enabled)
Select RGBA16F scene/effect intermediates while retaining the presentation format.
void setFrameReadbackEnabled(bool enabled) noexcept
Enable or disable readback of each rendered swapchain frame.
VK_Sprite * attachPostProcessingShader(const std::string &fragmentShaderPath, float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
Attach a full-screen post-processing fragment shader.
void printPreviewText(const std::string &text, int x, int y, const SDL_Color &col)
Queue preview-only text after frame readback.
bool ensureRenderResources()
Ensure deferred render resources are initialized.
void detachPostProcessingShader()
Detach the current post-processing shader and return to direct swapchain rendering.
void setPostProcessingTextureConsumerEnabled(bool enabled) noexcept
Route the completed effect chain to a derived renderer as a sampled texture.
void release()
Release Vulkan and SDL resources.
void setFont(const std::string &fontPath, int fontSize=24)
Set the active text-render font.
void setPostProcessingEnabled(bool enabled)
void printText(const std::string &text, int x, int y, const SDL_Color &col)
Queue a text string for rendering during the current frame.
void trimMemory()
Return transient Vulkan command-pool allocations to the driver when supported.
void setPostProcessingShaderParams(float p1=0.0f, float p2=0.0f, float p3=0.0f, float p4=0.0f)
Set the post-processing shader params passed as a vec4 push constant.
virtual void render()
Render one frame.
bool headless() const noexcept
Return true when rendering without SDL video, a surface, or presentation.
void setPostProcessingPresentFragmentShader(const std::string &path)
Set an optional fragment shader used only when presenting the final post-processing texture to a wind...
virtual void proc()
Execute one processing/update step.
bool validationEnabled() const
Check whether Vulkan validation layers are currently enabled.
void setPostProcessingShaderTimeEnabled(bool enabled)
Keep shader param 1 updated with elapsed render time in seconds.
void setEnableScreenshot(bool enabled) noexcept
Enable or disable F10 screenshot capture.
void setPreviewFont(const std::string &fontPath, int fontSize=24)
Set the preview-only HUD font independently of output text.
std::array< float, 16 > matrix_to_rows(const glm::mat4 &matrix)
glm::mat4 matrix_from_rows(const std::array< float, 16 > &values)
std::array< float, 4 > vector4_array(const glm::vec4 &value)
glm::vec4 vector4(const std::array< float, 4 > &values)
void setDefaultShaderDirectory(const std::string &directory)
Owned Vulkan buffer allocation with optional persistent host mapping.
VkDeviceSize size
Requested buffer size in bytes.
Parsed subset of Wavefront MTL material fields.
Sprite-compatible fragment parameters for UV-based model effects.
Vertex layout consumed by VK_PointSpriteBatch.
float size
Rasterized point size in pixels, passed to shader location 1.
bool usesSpectrumHistoryTexture
StorageImageFormat storageImageFormat
Owned sampled 2D texture resources.
uint32_t width
Texture width in pixels.
uint32_t height
Texture height in pixels.
Vertex payload consumed by MXVK model shaders.
float time
Elapsed animation time in seconds.
float phase
User-defined phase value for animated stencil/content shaders.
float aspect
Render target width divided by height.
float scale
User-defined scale value for stencil/content shaders.
std::array< float, 4 > params
uint32_t spectrumHistoryLayerCount
VK_Sprite * historySource
uint32_t spectrumBinCount
std::string fragmentShaderPath
Minimal Vulkan handles required by MXVK resource helpers.