Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,4 @@ if(SUPPORT_GLSL_PARSER AND SUPPORT_WGSL_PARSER)
add_cpp_example(glsl_to_wgsl)
endif()
add_cpp_example(textures_storage)
add_example(textures_cubemap)
126 changes: 126 additions & 0 deletions examples/textures_cubemap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include <raygpu.h>
#include <stdlib.h>
#include <string.h>

const char cubemapVS[] = "#version 450\n"
"layout(location = 0) in vec3 in_position;\n"
"layout(location = 1) in vec2 in_uv;\n"
"layout(location = 2) in vec3 in_normal;\n"
"layout(location = 3) in vec4 in_color;\n"
"\n"
"layout(location = 0) out vec3 localPos;\n"
"\n"
"layout(binding = 0) uniform Perspective_View {\n"
" mat4 pvmatrix;\n"
"};\n"
"\n"
"void main() {\n"
" gl_Position = pvmatrix * vec4(in_position, 1.0);\n"
" localPos = in_position;\n"
"}\n";

const char cubemapFS[] = "#version 450\n"
"layout(location = 0) in vec3 localPos;\n"
"\n"
"layout(location = 0) out vec4 outColor;\n"
"\n"
"layout(binding = 1) uniform textureCube cubeTexture;\n"
"layout(binding = 2) uniform sampler cubeSampler;\n"
"\n"
"void main() {\n"
" outColor = texture(samplerCube(cubeTexture, cubeSampler), normalize(localPos));\n"
"}\n";

Camera3D cam;
Mesh cube;
Shader pl;
TextureCubemap cubemap;
DescribedSampler smp;
float angle;

static Image GenCubemapStrip(int faceSize){
int w = faceSize * 6;
int h = faceSize;
Color* pixels = (Color*)RL_CALLOC((size_t)w * h, sizeof(Color));

Color faceColors[6] = {
{255, 80, 80, 255},
{ 80, 255, 80, 255},
{ 80, 80, 255, 255},
{255, 255, 80, 255},
{255, 80, 255, 255},
{ 80, 255, 255, 255},
};

for(int face = 0; face < 6; face++){
for(int y = 0; y < faceSize; y++){
for(int x = 0; x < faceSize; x++){
int px = face * faceSize + x;
int border = (x == 0 || x == faceSize - 1 || y == 0 || y == faceSize - 1);
if(border){
pixels[y * w + px] = (Color){40, 40, 40, 255};
} else {
pixels[y * w + px] = faceColors[face];
}
}
}
}

return (Image){
.data = pixels,
.width = w,
.height = h,
.mipmaps = 1,
.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
.rowStrideInBytes = (size_t)w * 4,
};
}

void setup(){
cam = CLITERAL(Camera3D){
.position = CLITERAL(Vector3){0, 0, 5},
.target = CLITERAL(Vector3){0, 0, 0},
.up = CLITERAL(Vector3){0, 1, 0},
.fovy = 60.0f,
};

cube = GenMeshCube(2.f, 2.f, 2.f);
pl = LoadShaderFromMemory(cubemapVS, cubemapFS);
PrepareShader(pl, cube.vao);

Image strip = GenCubemapStrip(64);
cubemap = LoadTextureCubemap(strip, CUBEMAP_LAYOUT_LINE_HORIZONTAL);
UnloadImage(strip);

smp = LoadSampler(TEXTURE_WRAP_CLAMP, TEXTURE_FILTER_BILINEAR);
SetShaderTexture(pl, GetUniformLocation(pl, "cubeTexture"), cubemap);
SetShaderSampler(pl, GetUniformLocation(pl, "cubeSampler"), smp);

angle = 0.0f;
}

void render(){
angle += GetFrameTime();
cam.position = (Vector3){sinf(angle) * 5.f, 2.0f, cosf(angle) * 5.f};

BeginDrawing();
ClearBackground(CLITERAL(Color){30, 30, 30, 255});
BeginShaderMode(pl);
BeginMode3D(cam);
BindVertexArray(cube.vao);
DrawArraysIndexed(RL_TRIANGLES, *cube.ibo, 36);
EndMode3D();
EndShaderMode();
DrawFPS(0, 0);
EndDrawing();
}

int main(void){
InitProgram((ProgramInfo){
.windowTitle = "Cubemap Texture",
.windowWidth = 800,
.windowHeight = 600,
.setupFunction = setup,
.renderFunction = render,
});
}
11 changes: 11 additions & 0 deletions include/raygpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ typedef struct Texture2D{
}Texture2D;

typedef Texture2D Texture;
typedef Texture TextureCubemap;

typedef struct Texture3D{
WGPUTexture id;
Expand Down Expand Up @@ -451,6 +452,7 @@ typedef enum uniform_type {
texture3d,
storage_texture3d,
storage_texture2d_array,
texture_cube,
acceleration_structure,
combined_image_sampler,
uniform_type_enumcount,
Expand Down Expand Up @@ -1104,6 +1106,14 @@ typedef enum {
#define MATERIAL_MAP_DIFFUSE MATERIAL_MAP_ALBEDO
#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS

typedef enum {
CUBEMAP_LAYOUT_AUTO_DETECT = 0,
CUBEMAP_LAYOUT_LINE_VERTICAL,
CUBEMAP_LAYOUT_LINE_HORIZONTAL,
CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR,
CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE,
} CubemapLayout;

typedef enum {
FONT_DEFAULT = 0, // Default font generation, anti-aliased
FONT_BITMAP, // Bitmap font generation, no anti-aliasing
Expand Down Expand Up @@ -1767,6 +1777,7 @@ RGAPI void UnloadRenderTexture(RenderTexture tex);
RGAPI size_t GetPixelSizeInBytes(PixelFormat format);
RGAPI Texture LoadBlankTexture(uint32_t width, uint32_t height);
RGAPI Texture LoadTexture(const char* filename);
RGAPI TextureCubemap LoadTextureCubemap(Image image, int layout);
RGAPI Texture LoadDepthTexture(uint32_t width, uint32_t height);
RGAPI Texture LoadTextureEx(uint32_t width, uint32_t height, PixelFormat format, bool to_be_used_as_rendertarget);
RGAPI Texture LoadTexturePro(uint32_t width, uint32_t height, PixelFormat format, RGTextureUsage usage, uint32_t sampleCount, uint32_t mipmaps);
Expand Down
177 changes: 177 additions & 0 deletions src/backend_wgpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,11 @@ WGPUBindGroupLayoutEntry toWGPUBindGroupLayoutEntry(const ResourceTypeDescriptor
ret.texture.sampleType = toTextureSampleType(rtd->fstype);
ret.texture.viewDimension = WGPUTextureViewDimension_3D;
break;
case texture_cube:
ret.visibility = shaderStage;
ret.texture.sampleType = toTextureSampleType(rtd->fstype);
ret.texture.viewDimension = WGPUTextureViewDimension_Cube;
break;
case storage_texture2d:
ret.storageTexture.access = toStorageTextureAccess(rtd->access);
ret.visibility = shaderStage;
Expand Down Expand Up @@ -1758,6 +1763,174 @@ Texture LoadTextureFromImage(Image img) {
TRACELOG(LOG_INFO, "Successfully loaded %u x %u texture from image", (unsigned)img.width, (unsigned)img.height);
return ret;
}

TextureCubemap LoadTextureCubemap(Image image, int layout){
if(image.data == NULL){
TRACELOG(LOG_WARNING, "LoadTextureCubemap: image data is NULL");
return (TextureCubemap){0};
}

// Auto-detect layout from aspect ratio
if(layout == CUBEMAP_LAYOUT_AUTO_DETECT){
if(image.width > image.height){
if((image.width / 6) == image.height){
layout = CUBEMAP_LAYOUT_LINE_HORIZONTAL;
} else if((image.width / 4) == (image.height / 3)){
layout = CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE;
} else {
layout = CUBEMAP_LAYOUT_LINE_HORIZONTAL;
}
} else {
if((image.height / 6) == image.width){
layout = CUBEMAP_LAYOUT_LINE_VERTICAL;
} else if((image.width / 3) == (image.height / 4)){
layout = CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR;
} else {
layout = CUBEMAP_LAYOUT_LINE_VERTICAL;
}
}
}

uint32_t faceSize = 0;

// Face positions: [col, row] for each of the 6 faces (+X, -X, +Y, -Y, +Z, -Z)
int faceX[6] = {0};
int faceY[6] = {0};

switch(layout){
case CUBEMAP_LAYOUT_LINE_VERTICAL: {
faceSize = image.width;
for(int i = 0; i < 6; i++){
faceX[i] = 0;
faceY[i] = (int)faceSize * i;
}
} break;
case CUBEMAP_LAYOUT_LINE_HORIZONTAL: {
faceSize = image.height;
for(int i = 0; i < 6; i++){
faceX[i] = (int)faceSize * i;
faceY[i] = 0;
}
} break;
case CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR: {
// 3 cols x 4 rows:
// [+Y] (row 0, col 1)
// [-X] [+Z] [+X] (row 1, cols 0,1,2)
// [-Y] (row 2, col 1)
// [-Z] (row 3, col 1)
faceSize = image.width / 3;
faceX[0] = (int)faceSize * 2; faceY[0] = (int)faceSize * 1; // +X
faceX[1] = (int)faceSize * 0; faceY[1] = (int)faceSize * 1; // -X
faceX[2] = (int)faceSize * 1; faceY[2] = (int)faceSize * 0; // +Y
faceX[3] = (int)faceSize * 1; faceY[3] = (int)faceSize * 2; // -Y
faceX[4] = (int)faceSize * 1; faceY[4] = (int)faceSize * 1; // +Z
faceX[5] = (int)faceSize * 1; faceY[5] = (int)faceSize * 3; // -Z
} break;
case CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE: {
// 4 cols x 3 rows:
// [+Y] (row 0, col 1)
// [-X] [+Z] [+X] [-Z] (row 1, cols 0,1,2,3)
// [-Y] (row 2, col 1)
faceSize = image.width / 4;
faceX[0] = (int)faceSize * 2; faceY[0] = (int)faceSize * 1; // +X
faceX[1] = (int)faceSize * 0; faceY[1] = (int)faceSize * 1; // -X
faceX[2] = (int)faceSize * 1; faceY[2] = (int)faceSize * 0; // +Y
faceX[3] = (int)faceSize * 1; faceY[3] = (int)faceSize * 2; // -Y
faceX[4] = (int)faceSize * 1; faceY[4] = (int)faceSize * 1; // +Z
faceX[5] = (int)faceSize * 3; faceY[5] = (int)faceSize * 1; // -Z
} break;
default: {
TRACELOG(LOG_WARNING, "LoadTextureCubemap: unknown layout %d", layout);
return (TextureCubemap){0};
}
}

if(faceSize == 0){
TRACELOG(LOG_WARNING, "LoadTextureCubemap: face size is zero");
return (TextureCubemap){0};
}

// Ensure image is RGBA8 for upload
Image imgCopy = ImageFromImage(image, CLITERAL(Rectangle){0, 0, (float)image.width, (float)image.height});
ImageFormat(&imgCopy, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);

WGPUTextureFormat wgpuFormat = WGPUTextureFormat_RGBA8Unorm;

WGPUTextureDescriptor tDesc = {
.usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst | WGPUTextureUsage_CopySrc,
.dimension = WGPUTextureDimension_2D,
.size = {faceSize, faceSize, 6},
.format = wgpuFormat,
.mipLevelCount = 1,
.sampleCount = 1,
.viewFormatCount = 1,
.viewFormats = &wgpuFormat,
};

WGPUTexture gpuTex = wgpuDeviceCreateTexture(GetDevice(), &tDesc);
if(gpuTex == NULL){
TRACELOG(LOG_WARNING, "LoadTextureCubemap: wgpuDeviceCreateTexture failed");
UnloadImage(imgCopy);
return (TextureCubemap){0};
}

TextureCubemap ret = {
.id = gpuTex,
.width = faceSize,
.height = faceSize,
.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
.sampleCount = 1,
.mipmaps = 1,
};

uint32_t bytesPerPixel = 4;
uint64_t faceBytes = (uint64_t)faceSize * faceSize * bytesPerPixel;
uint8_t* allFaces = (uint8_t*)RL_CALLOC(faceBytes * 6, 1);

for(int face = 0; face < 6; face++){
uint8_t* faceDst = allFaces + face * faceBytes;
for(uint32_t y = 0; y < faceSize; y++){
uint8_t* src = (uint8_t*)imgCopy.data + ((faceY[face] + y) * imgCopy.rowStrideInBytes) + (faceX[face] * bytesPerPixel);
uint8_t* dst = faceDst + (y * faceSize * bytesPerPixel);
memcpy(dst, src, faceSize * bytesPerPixel);
}
}

const WGPUTexelCopyTextureInfo destination = {
.texture = (WGPUTexture)ret.id,
.mipLevel = 0,
.origin = {0, 0, 0},
.aspect = WGPUTextureAspect_All,
};

const WGPUTexelCopyBufferLayout source = {
.offset = 0,
.bytesPerRow = faceSize * bytesPerPixel,
.rowsPerImage = faceSize,
};

const WGPUExtent3D writeSize = {faceSize, faceSize, 6};
wgpuQueueWriteTexture(GetQueue(), &destination, allFaces, faceBytes * 6, &source, &writeSize);

RL_FREE(allFaces);
UnloadImage(imgCopy);

const WGPUTextureViewDescriptor vDesc = {
.format = wgpuFormat,
.dimension = WGPUTextureViewDimension_Cube,
.baseMipLevel = 0,
.mipLevelCount = 1,
.baseArrayLayer = 0,
.arrayLayerCount = 6,
.aspect = WGPUTextureAspect_All,
};

ret.view = wgpuTextureCreateView((WGPUTexture)ret.id, &vDesc);

TRACELOG(LOG_INFO, "Successfully loaded %u x %u cubemap texture", faceSize, faceSize);
return ret;
}

void ResizeSurface(FullSurface *fsurface, int newWidth, int newHeight) {
fsurface->renderTarget.colorMultisample.width = newWidth;
fsurface->renderTarget.colorMultisample.height = newHeight;
Expand Down Expand Up @@ -2762,13 +2935,17 @@ static inline uniform_type uniform_type_from_binding(const SpvReflectDescriptorB
switch (b->image.dim) {
case SpvDim3D:
return texture3d;
case SpvDimCube:
return texture_cube;
default:
return b->image.arrayed ? texture2d_array : texture2d;
}
case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
switch (b->image.dim) {
case SpvDim3D:
return texture3d;
case SpvDimCube:
return texture_cube;
default:
return b->image.arrayed ? texture2d_array : texture2d;
}
Expand Down
12 changes: 12 additions & 0 deletions src/shader_parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,18 @@ StringToUniformMap* getBindingsWGSL_Tint(ShaderSources sources) {
out.emplace(name, desc);
continue;
}
if (tname.rfind("texture_cube", 0) == 0) {
desc.type = texture_cube;

if (auto* tid = id->As<tint::ast::TemplatedIdentifier>()) {
if (tid->arguments.Length() >= 1) {
if (auto* a0 = tid->arguments[0]->As<tint::ast::IdentifierExpression>())
desc.fstype = ti_parse_format(a0->identifier->symbol.Name());
}
}
out.emplace(name, desc);
continue;
}
if (tname.rfind("texture_2d", 0) == 0 || tname.rfind("texture_3d", 0) == 0) {
bool is2d = tname.rfind("texture_2d", 0) == 0;
bool is3d = tname.rfind("texture_3d", 0) == 0;
Expand Down
Loading
Loading