pp0.png
[Hide] (34.2KB, 804x638) pp1.png
[Hide] (35KB, 804x638) pp2.png
[Hide] (35.4KB, 804x638) crate.png
[Hide] (11.8KB, 128x128) goblin16.png
[Hide] (4.3KB, 128x256) >>562
Implemented one of the vertical slice/proof-of-concept features today - pixel-perfect object selection. The selection is done in two steps: the first is to see if the mouse click coordinates are within an object's axis-aligned bounding box (AABB), for which I'm just using the texture size for right now (eventually I will use a per-image settable AABB, to reduce the number of pixel-selected searches, since they are far more expensive. The second step is to load the image data from the GPU texture back into main RAM, then query the pixel at the click location to see if it's alpha channel value is higher than 0 - in other words, is not a transparent pixel. When an object is selected, I then use a shader (taken unedited from Raylib example ht tps://github.com/raysan5/raylib/blob/master/examples/shaders/shaders_texture_outline.c) to draw an outline around it. main.cpp is as follows:
#include "main.h"
#include "raylib.h"
#include "debug.h"
#include "rlgl.h"
void DrawTextureSimple(Texture2D & texture, Vector2 origin, Color tint, float scale)
{
if (texture.id > 0)
{
float width = (float)texture.width * scale;
float height = (float)texture.height * scale;
rlSetTexture(texture.id);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlTexCoord2f(0.0f, 0.0f);
rlVertex2f(origin.x, origin.y);
rlTexCoord2f(0.0f, 1.0f);
rlVertex2f(origin.x, origin.y + height);
rlTexCoord2f(1.0f, 1.0f);
rlVertex2f(origin.x + width, origin.y + height);
rlTexCoord2f(1.0f, 0.0f);
rlVertex2f(origin.x + width, origin.y);
rlEnd();
rlSetTexture(0);
}
}
class PickableObject
{
public:
std::string name;
Texture2D texture;
Shader outline_shader;
Vector2 center;
bool within_aabb = false;
bool selected = false;
PickableObject(std::string name, std::string texture_file, Vector2 center);
void draw(void);
void check_collision(Vector2 mouse_coords);
};
PickableObject::PickableObject(std::string name, std::string texture_file, Vector2 center)
{
this->name = name;
this->center = center;
#ifdef DEBUG
debug_out("ATTEMPT TO OPEN PATH: " + std::string(TextFormat("data/%s.png", texture_file.c_str())));
#endif
Image img = LoadImage(TextFormat("data/%s.png", texture_file.c_str()));
texture = LoadTextureFromImage(img);
UnloadImage(img);
outline_shader = LoadShader(0, "data/shaders/outline.fs");
float outlineSize = 2.0f;
float outlineColor[4] = {1.0f, 0.0f, 0.0f, 1.0f};
float textureSize[2] = { (float)texture.width, (float)texture.height };
int outlineSizeLoc = GetShaderLocation(outline_shader, "outlineSize");
int outlineColorLoc = GetShaderLocation(outline_shader, "outlineColor");
int textureSizeLoc = GetShaderLocation(outline_shader, "textureSize");
SetShaderValue(outline_shader, outlineSizeLoc, &outlineSize, SHADER_UNIFORM_FLOAT);
SetShaderValue(outline_shader, outlineColorLoc, outlineColor, SHADER_UNIFORM_VEC4);
SetShaderValue(outline_shader, textureSizeLoc, textureSize, SHADER_UNIFORM_VEC2);
}
void PickableObject::draw(void)
{
float draw_x = center.x - ((float)texture.width / 2);
float draw_y = center.y - ((float)texture.height / 2);
if (selected)
{
BeginShaderMode(outline_shader);
DrawTextureSimple(texture, {draw_x, draw_y}, WHITE, 1.0f);
EndShaderMode();
}
else
{
DrawTextureSimple(texture, {draw_x, draw_y}, WHITE, 1.0f);
}
if (within_aabb)
{
DrawRectangleLines(draw_x, draw_y, texture.width, texture.height, WHITE);
}
}
void PickableObject::check_collision(Vector2 mouse_coords)
{
if (mouse_coords.x > (center.x - ((float)texture.width / 2)) && mouse_coords.x < (center.x + ((float)texture.width / 2)))
{
if (mouse_coords.y > (center.y - ((float)texture.height / 2)) && mouse_coords.y < (center.y + ((float)texture.height / 2)))
{
#ifdef DEBUG
debug_out("CLICKED WITHIN AABB OF " + std::string(TextFormat("%s", name.c_str())));
#endif
within_aabb = true;
Image img = LoadImageFromTexture(texture);
int pixel_x = (int)mouse_coords.x - ((int)center.x - (texture.width / 2));
int pixel_y = (int)mouse_coords.y - ((int)center.y - (texture.height / 2));
#ifdef DEBUG
debug_out("CLICKED IMAGE PIXEL COORDS: " + std::to_string(pixel_x) + ", " + std::to_string(pixel_y));
#endif
Color pixel = GetImageColor(img, pixel_x, pixel_y);
if (pixel.a > 0)
{
#ifdef DEBUG
debug_out(std::string(TextFormat("%s", name.c_str())) + " SELECTED");
#endif
selected = true;
}
else
{
selected = false;
}
UnloadImage(img);
return;
}
}
within_aabb = false;
selected = false;
}
int main(int argc, char *argv[])
{
SetWindowState(FLAG_MSAA_4X_HINT);
SetWindowState(FLAG_VSYNC_HINT);
InitWindow(WIN_WIDTH, WIN_HEIGHT, "PIXEL PICKING");
center_window();
PickableObject gob = {"goblin", "goblin16", {250, 300}};
PickableObject crate = {"crate", "crate", {550, 300}};
while (!WindowShouldClose())
{
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
{
Vector2 mouse = GetMousePosition();
gob.check_collision(mouse);
crate.check_collision(mouse);
}
BeginDrawing();
ClearBackground(BLACK);
gob.draw();
crate.draw();
EndDrawing();
}
CloseWindow();
}
void center_window(void)
{
int display = GetCurrentMonitor();
int display_width = GetMonitorWidth(display);
int display_height = GetMonitorHeight(display);
SetWindowPosition((display_width / 2) - (GetScreenWidth() / 2), (display_height / 2) - (GetScreenHeight() / 2));
}
The shader code file, outline.fs, is from the example:
#version 330
in vec2 fragTexCoord;
in vec4 fragColor;
uniform sampler2D texture0;
uniform vec4 colDiffuse;
uniform vec2 textureSize;
uniform float outlineSize;
uniform vec4 outlineColor;
out vec4 finalColor;
void main()
{
vec4 texel = texture(texture0, fragTexCoord);
vec2 texelScale = vec2(0.0);
texelScale.x = outlineSize/textureSize.x;
texelScale.y = outlineSize/textureSize.y;
vec4 corners = vec4(0.0);
corners.x = texture(texture0, fragTexCoord + vec2(texelScale.x, texelScale.y)).a;
corners.y = texture(texture0, fragTexCoord + vec2(texelScale.x, -texelScale.y)).a;
corners.z = texture(texture0, fragTexCoord + vec2(-texelScale.x, texelScale.y)).a;
corners.w = texture(texture0, fragTexCoord + vec2(-texelScale.x, -texelScale.y)).a;
float outline = min(dot(corners, vec4(1.0)), 1.0);
vec4 color = mix(vec4(0.0), outlineColor, outline);
finalColor = mix(color, texel, texel.a);
}
You're gonna need the attached images to make it run too.