7 std::cout <<
">> VKAbstractModel::load: model=" << model_file <<
", textures=" << texture_file <<
", scale=" << scale <<
"\n";
8 obj.load(model_file, scale);
9 const auto &verts =
obj.vertices();
11 float minX = verts[0].pos[0], maxX = minX;
12 float minY = verts[0].pos[1], maxY = minY;
13 float minZ = verts[0].pos[2], maxZ = minZ;
14 for (
const auto &v : verts) {
15 minX = std::min(minX, v.pos[0]); maxX = std::max(maxX, v.pos[0]);
16 minY = std::min(minY, v.pos[1]); maxY = std::max(maxY, v.pos[1]);
17 minZ = std::min(minZ, v.pos[2]); maxZ = std::max(maxZ, v.pos[2]);
20 -0.5f * (minX + maxX),
21 -0.5f * (minY + maxY),
22 -0.5f * (minZ + maxZ));
23 float maxExtent = std::max(maxX - minX, std::max(maxY - minY, maxZ - minZ));
24 if (maxExtent > 1e-6f)
35 for (
size_t i = 0; i < frameCount; ++i) {
37 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
38 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
46 std::cout <<
">> VKAbstractModel::load: complete [OK]\n";
120 std::cout <<
">> VKAbstractModel::loadModelTextures: loading from " << texture_file <<
"\n";
121 std::string texPath = texture_file;
122 std::ifstream tf(texPath);
126 std::string prefix = path;
127 std::vector<std::string> imagePaths;
130 std::vector<std::string> lines;
133 while (std::getline(tf, line)) {
134 size_t b = line.find_first_not_of(
" \t\r\n");
135 if (b == std::string::npos)
continue;
136 size_t e = line.find_last_not_of(
" \t\r\n");
137 line = line.substr(b, e - b + 1);
138 if (line.empty() || line[0] ==
'#')
continue;
139 lines.push_back(line);
144 bool structured =
false;
146 for (
const auto &ln : lines) {
147 std::istringstream chk(ln);
150 if (kw ==
"submesh" || kw ==
"texture_dir" || kw ==
"material_lib" || kw ==
"model") {
154 if (kw ==
"newmtl") {
161 std::cout <<
">> VKAbstractModel::loadModelTextures: MTL format detected, extracting map_Kd textures\n";
162 for (
const auto &ln : lines) {
163 std::istringstream s(ln);
166 if (kw ==
"map_Kd") {
169 imagePaths.push_back(prefix +
"/" + texFile);
172 }
else if (structured) {
173 std::cout <<
">> VKAbstractModel::loadModelTextures: structured .tex format detected\n";
174 for (
const auto &ln : lines) {
175 std::istringstream s(ln);
178 if (kw ==
"texture") {
181 imagePaths.push_back(prefix +
"/" + texFile);
189 for (
const auto &ln : lines)
190 imagePaths.push_back(prefix +
"/" + ln);
193 if (imagePaths.empty())
196 VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
197 for (
size_t i = 0; i < imagePaths.size(); ++i) {
200 img = SDL_CreateRGBSurfaceWithFormat(0, 1, 1, 32, SDL_PIXELFORMAT_RGBA32);
201 if (img) { uint32_t *px = (uint32_t*)img->pixels; *px = 0xFFFFFFFF; }
202 else throw mx::Exception(
"Failed to create placeholder surface");
208 VkDeviceSize imageSize = tex.
w * tex.
h * 4;
211 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
212 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, tex.
image, tex.
memory);
215 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
217 VkBuffer staging; VkDeviceMemory stagingMem;
218 win->
createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
219 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
220 staging, stagingMem);
222 vkMapMemory(win->
getDevice(), stagingMem, 0, imageSize, 0, &data);
223 memcpy(data, img->pixels, (
size_t)imageSize);
224 vkUnmapMemory(win->
getDevice(), stagingMem);
228 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
230 vkDestroyBuffer(win->
getDevice(), staging,
nullptr);
231 vkFreeMemory(win->
getDevice(), stagingMem,
nullptr);
232 SDL_FreeSurface(img);
236 std::cout <<
">> VKAbstractModel::loadModelTextures: [" << i <<
"] " << imagePaths[i] <<
" (" << tex.
w <<
"x" << tex.
h <<
")\n";
238 std::cout <<
">> VKAbstractModel::loadModelTextures: " <<
modelTextures.size() <<
" texture(s) loaded [OK]\n";
242 const auto &mats =
obj.materials();
244 std::cerr <<
">> VKAbstractModel::loadModelTexturesFromMTL: no materials loaded\n";
247 std::cout <<
">> VKAbstractModel::loadModelTexturesFromMTL: loading " << mats.size() <<
" texture(s) from MTL map_Kd\n";
248 std::string prefix = path;
251 if (!
obj.mtlLibPath().empty()) {
252 auto pos =
obj.mtlLibPath().find_last_of(
"/\\");
253 if (pos != std::string::npos)
254 mtlDir =
obj.mtlLibPath().substr(0, pos);
257 VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
258 for (
size_t i = 0; i < mats.size(); ++i) {
260 if (!mats[i].map_kd.empty()) {
262 imgPath = prefix +
"/" + mats[i].map_kd;
265 SDL_Surface *img =
nullptr;
266 if (!imgPath.empty())
271 img = SDL_CreateRGBSurfaceWithFormat(0, 1, 1, 32, SDL_PIXELFORMAT_RGBA32);
273 uint8_t r =
static_cast<uint8_t
>(std::min(1.0f, mats[i].kd[0]) * 255.0f);
274 uint8_t g =
static_cast<uint8_t
>(std::min(1.0f, mats[i].kd[1]) * 255.0f);
275 uint8_t b =
static_cast<uint8_t
>(std::min(1.0f, mats[i].kd[2]) * 255.0f);
276 uint32_t *px = (uint32_t*)img->pixels;
277 *px = (0xFF << 24) | (b << 16) | (g << 8) | r;
279 throw mx::Exception(
"Failed to create placeholder surface for material " + mats[i].name);
286 VkDeviceSize imageSize = tex.
w * tex.
h * 4;
289 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
290 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, tex.
image, tex.
memory);
293 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
295 VkBuffer staging; VkDeviceMemory stagingMem;
296 win->
createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
297 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
298 staging, stagingMem);
300 vkMapMemory(win->
getDevice(), stagingMem, 0, imageSize, 0, &data);
301 memcpy(data, img->pixels, (
size_t)imageSize);
302 vkUnmapMemory(win->
getDevice(), stagingMem);
306 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
308 vkDestroyBuffer(win->
getDevice(), staging,
nullptr);
309 vkFreeMemory(win->
getDevice(), stagingMem,
nullptr);
310 SDL_FreeSurface(img);
314 std::cout <<
">> VKAbstractModel::loadModelTexturesFromMTL: [" << i <<
"] "
315 << mats[i].name <<
" -> " << (imgPath.empty() ?
"(Kd placeholder)" : imgPath)
316 <<
" (" << tex.
w <<
"x" << tex.
h <<
")\n";
318 std::cout <<
">> VKAbstractModel::loadModelTexturesFromMTL: " <<
modelTextures.size() <<
" texture(s) loaded [OK]\n";
372 std::cout <<
">> VKAbstractModel::createModelDescriptorSets: allocating sets...\n";
373 const size_t texCount = std::max<size_t>(1,
modelTextures.size());
377 VkDescriptorSetAllocateInfo ai{};
378 ai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
380 ai.descriptorSetCount =
static_cast<uint32_t
>(setCount);
381 ai.pSetLayouts = layouts.data();
384 throw mx::Exception(
"Failed to allocate model descriptor sets!");
387 VkDescriptorBufferInfo bufInfo{};
392 for (
size_t tex = 0; tex < texCount; ++tex) {
393 size_t setIndex = frame * texCount + tex;
395 VkDescriptorImageInfo imgInfo{};
396 imgInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
400 std::array<VkWriteDescriptorSet, 2> writes{};
401 writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
403 writes[0].dstBinding = 0;
404 writes[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
405 writes[0].descriptorCount = 1;
406 writes[0].pImageInfo = &imgInfo;
408 writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
410 writes[1].dstBinding = 1;
411 writes[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
412 writes[1].descriptorCount = 1;
413 writes[1].pBufferInfo = &bufInfo;
415 vkUpdateDescriptorSets(win->
getDevice(),
static_cast<uint32_t
>(writes.size()),
416 writes.data(), 0,
nullptr);
419 std::cout <<
">> VKAbstractModel::createModelDescriptorSets: " << setCount <<
" set(s) allocated [OK]\n";
481 std::cout <<
">> VKAbstractModel::createModelPipeline: creating pipeline...\n";
489 VkPipelineShaderStageCreateInfo vertStage{};
490 vertStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
491 vertStage.stage = VK_SHADER_STAGE_VERTEX_BIT;
492 vertStage.module = vertModule;
493 vertStage.pName =
"main";
495 VkPipelineShaderStageCreateInfo fragStage{};
496 fragStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
497 fragStage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
498 fragStage.module = fragModule;
499 fragStage.pName =
"main";
501 VkPipelineShaderStageCreateInfo stages[] = { vertStage, fragStage };
503 VkVertexInputBindingDescription binding{};
505 binding.stride =
sizeof(
Vertex);
506 binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
508 std::array<VkVertexInputAttributeDescription, 3> attrs{};
509 attrs[0].binding = 0; attrs[0].location = 0;
510 attrs[0].format = VK_FORMAT_R32G32B32_SFLOAT;
511 attrs[0].offset = offsetof(
Vertex, pos);
512 attrs[1].binding = 0; attrs[1].location = 1;
513 attrs[1].format = VK_FORMAT_R32G32_SFLOAT;
514 attrs[1].offset = offsetof(
Vertex, texCoord);
515 attrs[2].binding = 0; attrs[2].location = 2;
516 attrs[2].format = VK_FORMAT_R32G32B32_SFLOAT;
517 attrs[2].offset = offsetof(
Vertex, normal);
519 VkPipelineVertexInputStateCreateInfo vertexInput{};
520 vertexInput.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
521 vertexInput.vertexBindingDescriptionCount = 1;
522 vertexInput.pVertexBindingDescriptions = &binding;
523 vertexInput.vertexAttributeDescriptionCount =
static_cast<uint32_t
>(attrs.size());
524 vertexInput.pVertexAttributeDescriptions = attrs.data();
526 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
527 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
528 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
529 inputAssembly.primitiveRestartEnable = VK_FALSE;
531 VkPipelineViewportStateCreateInfo viewportState{};
532 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
533 viewportState.viewportCount = 1;
534 viewportState.scissorCount = 1;
536 std::array<VkDynamicState, 2> dynStates = {
537 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR
539 VkPipelineDynamicStateCreateInfo dynState{};
540 dynState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
541 dynState.dynamicStateCount =
static_cast<uint32_t
>(dynStates.size());
542 dynState.pDynamicStates = dynStates.data();
544 VkPipelineRasterizationStateCreateInfo rasterizer{};
545 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
546 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
547 rasterizer.lineWidth = 1.0f;
548 rasterizer.cullMode = VK_CULL_MODE_NONE;
549 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
551 VkPipelineMultisampleStateCreateInfo multisampling{};
552 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
553 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
555 VkPipelineDepthStencilStateCreateInfo depthStencil{};
556 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
557 depthStencil.depthTestEnable = VK_TRUE;
558 depthStencil.depthWriteEnable = VK_TRUE;
559 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
561 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
562 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
563 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
564 colorBlendAttachment.blendEnable = VK_FALSE;
566 VkPipelineColorBlendStateCreateInfo colorBlending{};
567 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
568 colorBlending.attachmentCount = 1;
569 colorBlending.pAttachments = &colorBlendAttachment;
571 VkGraphicsPipelineCreateInfo ci{};
572 ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
575 ci.pVertexInputState = &vertexInput;
576 ci.pInputAssemblyState = &inputAssembly;
577 ci.pViewportState = &viewportState;
578 ci.pRasterizationState = &rasterizer;
579 ci.pMultisampleState = &multisampling;
580 ci.pDepthStencilState = &depthStencil;
581 ci.pColorBlendState = &colorBlending;
582 ci.pDynamicState = &dynState;
587 if (vkCreateGraphicsPipelines(win->
getDevice(), VK_NULL_HANDLE, 1, &ci,
nullptr, &
modelPipeline) != VK_SUCCESS)
588 throw mx::Exception(
"VKAbstractModel: Failed to create model pipeline!");
590 rasterizer.polygonMode = VK_POLYGON_MODE_LINE;
595 vkDestroyShaderModule(win->
getDevice(), fragModule,
nullptr);
596 vkDestroyShaderModule(win->
getDevice(), vertModule,
nullptr);
598 std::cout <<
">> VKAbstractModel::createModelPipeline: pipeline created [OK]\n";