1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Merge remote-tracking branch 'upstream/master' into Visuals-Vol-2

This commit is contained in:
Gefüllte Taubenbrust 2025-01-04 11:19:11 +01:00
commit 78de371f2d
123 changed files with 1506 additions and 1956 deletions

View file

@ -34,13 +34,6 @@ body:
render: "true" render: "true"
validations: validations:
required: true required: true
- type: input
attributes:
label: Irrlicht device
description:
placeholder: "Example: X11"
validations:
required: false
- type: input - type: input
attributes: attributes:
label: Operating system and version label: Operating system and version
@ -69,7 +62,7 @@ body:
attributes: attributes:
label: Active renderer label: Active renderer
description: You can find this in the "About" tab in the main menu. description: You can find this in the "About" tab in the main menu.
placeholder: "Example: OpenGL 4.6.0" placeholder: "Example: ES 3.2 / ogles2 / X11"
validations: validations:
required: false required: false
- type: textarea - type: textarea

View file

@ -32,6 +32,27 @@ local function get_credits()
return json return json
end end
local function get_renderer_info()
local ret = {}
-- OpenGL version, stripped to just the important part
local s1 = core.get_active_renderer()
if s1:sub(1, 7) == "OpenGL " then
s1 = s1:sub(8)
end
local m = s1:match("^[%d.]+")
if not m then
m = s1:match("^ES [%d.]+")
end
ret[#ret+1] = m or s1
-- video driver
ret[#ret+1] = core.get_active_driver():lower()
-- irrlicht device
ret[#ret+1] = core.get_active_irrlicht_device():upper()
return table.concat(ret, " / ")
end
return { return {
name = "about", name = "about",
caption = fgettext("About"), caption = fgettext("About"),
@ -81,20 +102,12 @@ return {
"button_url[1.5,4.1;2.5,0.8;homepage;luanti.org;https://www.luanti.org/]" .. "button_url[1.5,4.1;2.5,0.8;homepage;luanti.org;https://www.luanti.org/]" ..
"hypertext[5.5,0.25;9.75,6.6;credits;" .. core.formspec_escape(hypertext) .. "]" "hypertext[5.5,0.25;9.75,6.6;credits;" .. core.formspec_escape(hypertext) .. "]"
-- Render information local active_renderer_info = fgettext("Active renderer:") .. "\n" ..
local active_renderer_info = fgettext("Active renderer:") .. " " .. core.formspec_escape(get_renderer_info())
core.formspec_escape(core.get_active_renderer())
fs = fs .. "style[label_button2;border=false]" .. fs = fs .. "style[label_button2;border=false]" ..
"button[0.1,6;5.3,0.5;label_button2;" .. active_renderer_info .. "]".. "button[0.1,6;5.3,1;label_button2;" .. active_renderer_info .. "]"..
"tooltip[label_button2;" .. active_renderer_info .. "]" "tooltip[label_button2;" .. active_renderer_info .. "]"
-- Irrlicht device information
local irrlicht_device_info = fgettext("Irrlicht device:") .. " " ..
core.formspec_escape(core.get_active_irrlicht_device())
fs = fs .. "style[label_button3;border=false]" ..
"button[0.1,6.5;5.3,0.5;label_button3;" .. irrlicht_device_info .. "]"..
"tooltip[label_button3;" .. irrlicht_device_info .. "]"
if PLATFORM == "Android" then if PLATFORM == "Android" then
fs = fs .. "button[0.5,5.1;4.5,0.8;share_debug;" .. fgettext("Share debug log") .. "]" fs = fs .. "button[0.5,5.1;4.5,0.8;share_debug;" .. fgettext("Share debug log") .. "]"
else else

View file

@ -112,6 +112,7 @@ local function get_formspec(tabview, name, tabdata)
local retval = local retval =
-- Search -- Search
"field[0.25,0.25;7,0.75;te_search;;" .. core.formspec_escape(tabdata.search_for) .. "]" .. "field[0.25,0.25;7,0.75;te_search;;" .. core.formspec_escape(tabdata.search_for) .. "]" ..
"tooltip[te_search;" .. fgettext("Possible filters\ngame:<name>\nmod:<name>\nplayer:<name>") .. "]" ..
"field_enter_after_edit[te_search;true]" .. "field_enter_after_edit[te_search;true]" ..
"container[7.25,0.25]" .. "container[7.25,0.25]" ..
"image_button[0,0;0.75,0.75;" .. core.formspec_escape(defaulttexturedir .. "search.png") .. ";btn_mp_search;]" .. "image_button[0,0;0.75,0.75;" .. core.formspec_escape(defaulttexturedir .. "search.png") .. ";btn_mp_search;]" ..
@ -271,19 +272,106 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
local function parse_search_input(input)
if not input:find("%S") then
return -- Return nil if nothing to search for
end
-- Search is not case sensitive
input = input:lower()
local query = {keywords = {}, mods = {}, players = {}}
-- Process quotation enclosed parts
input = input:gsub('(%S?)"([^"]*)"(%S?)', function(before, match, after)
if before == "" and after == "" then -- Also have be separated by spaces
table.insert(query.keywords, match)
return " "
end
return before..'"'..match..'"'..after
end)
-- Separate by space characters and handle special prefixes
-- (words with special prefixes need an exact match and none of them can contain spaces)
for word in input:gmatch("%S+") do
local mod = word:match("^mod:(.*)")
table.insert(query.mods, mod)
local player = word:match("^player:(.*)")
table.insert(query.players, player)
local game = word:match("^game:(.*)")
query.game = query.game or game
if not (mod or player or game) then
table.insert(query.keywords, word)
end
end
return query
end
-- Prepares the server to be used for searching
local function uncapitalize_server(server)
local function table_lower(t)
local r = {}
for i, s in ipairs(t or {}) do
r[i] = s:lower()
end
return r
end
return {
name = (server.name or ""):lower(),
description = (server.description or ""):lower(),
gameid = (server.gameid or ""):lower(),
mods = table_lower(server.mods),
clients_list = table_lower(server.clients_list),
}
end
-- Returns false if the query does not match
-- otherwise returns a number to adjust the sorting priority
local function matches_query(server, query)
-- Search is not case sensitive
server = uncapitalize_server(server)
-- Check if mods found
for _, mod in ipairs(query.mods) do
if table.indexof(server.mods, mod) < 0 then
return false
end
end
-- Check if players found
for _, player in ipairs(query.players) do
if table.indexof(server.clients_list, player) < 0 then
return false
end
end
-- Check if game matches
if query.game and query.game ~= server.gameid then
return false
end
-- Check if keyword found
local name_matches = true
local description_matches = true
for _, keyword in ipairs(query.keywords) do
name_matches = name_matches and server.name:find(keyword, 1, true)
description_matches = description_matches and server.description:find(keyword, 1, true)
end
return name_matches and 50 or description_matches and 0
end
local function search_server_list(input) local function search_server_list(input)
menudata.search_result = nil menudata.search_result = nil
if #serverlistmgr.servers < 2 then if #serverlistmgr.servers < 2 then
return return
end end
-- setup the keyword list -- setup the search query
local keywords = {} local query = parse_search_input(input)
for word in input:gmatch("%S+") do if not query then
table.insert(keywords, word:lower())
end
if #keywords == 0 then
return return
end end
@ -292,16 +380,9 @@ local function search_server_list(input)
-- Search the serverlist -- Search the serverlist
local search_result = {} local search_result = {}
for i, server in ipairs(serverlistmgr.servers) do for i, server in ipairs(serverlistmgr.servers) do
local name_matches, description_matches = true, true local match = matches_query(server, query)
for _, keyword in ipairs(keywords) do if match then
name_matches = name_matches and not not server.points = #serverlistmgr.servers - i + match
(server.name or ""):lower():find(keyword, 1, true)
description_matches = description_matches and not not
(server.description or ""):lower():find(keyword, 1, true)
end
if name_matches or description_matches then
server.points = #serverlistmgr.servers - i
+ (name_matches and 50 or 0)
table.insert(search_result, server) table.insert(search_result, server)
end end
end end
@ -395,7 +476,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
if fields.btn_mp_search or fields.key_enter_field == "te_search" then if fields.btn_mp_search or fields.key_enter_field == "te_search" then
tabdata.search_for = fields.te_search tabdata.search_for = fields.te_search
search_server_list(fields.te_search:lower()) search_server_list(fields.te_search)
if menudata.search_result then if menudata.search_result then
-- Note: This clears the selection if there are no results -- Note: This clears the selection if there are no results
set_selected_server(menudata.search_result[1]) set_selected_server(menudata.search_result[1])

View file

@ -406,6 +406,7 @@ bilinear_filter (Bilinear filtering) bool false
trilinear_filter (Trilinear filtering) bool false trilinear_filter (Trilinear filtering) bool false
# Use anisotropic filtering when looking at textures from an angle. # Use anisotropic filtering when looking at textures from an angle.
# This provides a significant improvement when used together with mipmapping.
anisotropic_filter (Anisotropic filtering) bool false anisotropic_filter (Anisotropic filtering) bool false
# Select the antialiasing method to apply. # Select the antialiasing method to apply.
@ -1881,6 +1882,10 @@ mesh_generation_interval (Mapblock mesh generation delay) int 0 0 50
# Value of 0 (default) will let Luanti autodetect the number of available threads. # Value of 0 (default) will let Luanti autodetect the number of available threads.
mesh_generation_threads (Mapblock mesh generation threads) int 0 0 8 mesh_generation_threads (Mapblock mesh generation threads) int 0 0 8
# All mesh buffers with less than this number of vertices will be merged
# during map rendering. This improves rendering performance.
mesh_buffer_min_vertices (Minimum vertex count for mesh buffers) int 100 0 1000
# True = 256 # True = 256
# False = 128 # False = 128
# Usable to make minimap smoother on slower machines. # Usable to make minimap smoother on slower machines.
@ -1902,12 +1907,11 @@ world_aligned_mode (World-aligned textures mode) enum enable disable,enable,forc
# Warning: This option is EXPERIMENTAL! # Warning: This option is EXPERIMENTAL!
autoscale_mode (Autoscaling mode) enum disable disable,enable,force autoscale_mode (Autoscaling mode) enum disable disable,enable,force
# When using bilinear/trilinear/anisotropic filters, low-resolution textures # When using bilinear/trilinear filtering, low-resolution textures
# can be blurred, so automatically upscale them with nearest-neighbor # can be blurred, so this option automatically upscales them to preserve
# interpolation to preserve crisp pixels. This sets the minimum texture size # crisp pixels. This defines the minimum texture size for the upscaled textures;
# for the upscaled textures; higher values look sharper, but require more # higher values look sharper, but require more memory.
# memory. Powers of 2 are recommended. This setting is ONLY applied if # This setting is ONLY applied if any of the mentioned filters are enabled.
# bilinear/trilinear/anisotropic filtering is enabled.
# This is also used as the base node texture size for world-aligned # This is also used as the base node texture size for world-aligned
# texture autoscaling. # texture autoscaling.
texture_min_size (Base texture size) int 64 1 32768 texture_min_size (Base texture size) int 64 1 32768

View file

@ -9,6 +9,7 @@ attribute vec2 inTexCoord0;
/* Uniforms */ /* Uniforms */
uniform float uThickness; uniform float uThickness;
uniform mat4 uProjection;
/* Varyings */ /* Varyings */
@ -17,7 +18,7 @@ varying vec4 vVertexColor;
void main() void main()
{ {
gl_Position = inVertexPosition; gl_Position = uProjection * inVertexPosition;
gl_PointSize = uThickness; gl_PointSize = uThickness;
vTextureCoord = inTexCoord0; vTextureCoord = inTexCoord0;
vVertexColor = inVertexColor.bgra; vVertexColor = inVertexColor.bgra;

View file

@ -23,7 +23,12 @@ void main(void)
vec2 uv = varTexCoord.st; vec2 uv = varTexCoord.st;
vec3 color = texture2D(rendered, uv).rgb; vec3 color = texture2D(rendered, uv).rgb;
// translate to linear colorspace (approximate) // translate to linear colorspace (approximate)
#ifdef GL_ES
// clamp color to [0,1] range in lieu of centroids
color = pow(clamp(color, 0.0, 1.0), vec3(2.2)); color = pow(clamp(color, 0.0, 1.0), vec3(2.2));
#else
color = pow(color, vec3(2.2));
#endif
color *= exposureParams.compensationFactor * bloomStrength; color *= exposureParams.compensationFactor * bloomStrength;

View file

@ -43,11 +43,14 @@ varying vec3 vPosition;
// cameraOffset + worldPosition (for large coordinates the limits of float // cameraOffset + worldPosition (for large coordinates the limits of float
// precision must be considered). // precision must be considered).
varying vec3 worldPosition; varying vec3 worldPosition;
varying lowp vec4 varColor;
#ifdef GL_ES #ifdef GL_ES
varying lowp vec4 varColor;
varying mediump vec2 varTexCoord; varying mediump vec2 varTexCoord;
varying float nightRatio;
#else #else
centroid varying lowp vec4 varColor;
centroid varying vec2 varTexCoord; centroid varying vec2 varTexCoord;
centroid varying float nightRatio;
#endif #endif
varying highp vec3 eyeVec; varying highp vec3 eyeVec;
varying float nightRatio; varying float nightRatio;

View file

@ -14,14 +14,17 @@ varying vec3 vPosition;
// cameraOffset + worldPosition (for large coordinates the limits of float // cameraOffset + worldPosition (for large coordinates the limits of float
// precision must be considered). // precision must be considered).
varying vec3 worldPosition; varying vec3 worldPosition;
varying lowp vec4 varColor;
// The centroid keyword ensures that after interpolation the texture coordinates // The centroid keyword ensures that after interpolation the texture coordinates
// lie within the same bounds when MSAA is en- and disabled. // lie within the same bounds when MSAA is en- and disabled.
// This fixes the stripes problem with nearest-neighbor textures and MSAA. // This fixes the stripes problem with nearest-neighbor textures and MSAA.
#ifdef GL_ES #ifdef GL_ES
varying lowp vec4 varColor;
varying mediump vec2 varTexCoord; varying mediump vec2 varTexCoord;
varying float nightRatio;
#else #else
centroid varying lowp vec4 varColor;
centroid varying vec2 varTexCoord; centroid varying vec2 varTexCoord;
centroid varying float nightRatio;
#endif #endif
#ifdef ENABLE_DYNAMIC_SHADOWS #ifdef ENABLE_DYNAMIC_SHADOWS
// shadow uniforms // shadow uniforms
@ -47,6 +50,7 @@ varying highp vec3 eyeVec;
varying float nightRatio; varying float nightRatio;
varying vec3 sunTint; varying vec3 sunTint;
varying float nightFactor; varying float nightFactor;
// Color of the light emitted by the light sources. // Color of the light emitted by the light sources.
uniform vec3 artificialLight; uniform vec3 artificialLight;
const float e = 2.718281828459; const float e = 2.718281828459;

View file

@ -188,7 +188,6 @@ Mod directory structure
│   ├── models │   ├── models
│   ├── textures │   ├── textures
│   │   ├── modname_stuff.png │   │   ├── modname_stuff.png
│   │   ├── modname_stuff_normal.png
│   │   ├── modname_something_else.png │   │   ├── modname_something_else.png
│   │   ├── subfolder_foo │   │   ├── subfolder_foo
│   │   │ ├── modname_more_stuff.png │   │   │ ├── modname_more_stuff.png
@ -318,7 +317,7 @@ Many glTF features are not supported *yet*, including:
* Animations * Animations
* Only a single animation is supported, use frame ranges within this animation. * Only a single animation is supported, use frame ranges within this animation.
* Only linear interpolation is supported. * `CUBICSPLINE` interpolation is not supported.
* Cameras * Cameras
* Materials * Materials
* Only base color textures are supported * Only base color textures are supported

View file

@ -55,6 +55,20 @@ core.register_entity("gltf:simple_skin", {
end end
}) })
core.register_entity("gltf:simple_skin_step", {
initial_properties = {
infotext = "Simple skin, but using STEP interpolation",
visual = "mesh",
visual_size = vector.new(5, 5, 5),
mesh = "gltf_simple_skin_step.gltf",
textures = {},
backface_culling = false
},
on_activate = function(self)
self.object:set_animation({x = 0, y = 5.5}, 1)
end
})
-- The claws rendering incorrectly from one side is expected behavior: -- The claws rendering incorrectly from one side is expected behavior:
-- They use an unsupported double-sided material. -- They use an unsupported double-sided material.
core.register_entity("gltf:frog", { core.register_entity("gltf:frog", {

View file

@ -0,0 +1 @@
{"scene":0,"scenes":[{"nodes":[0,1]}],"nodes":[{"skin":0,"mesh":0},{"children":[2]},{"translation":[0.0,1.0,0.0],"rotation":[0.0,0.0,0.0,1.0]}],"meshes":[{"primitives":[{"attributes":{"POSITION":1,"JOINTS_0":2,"WEIGHTS_0":3},"indices":0}]}],"skins":[{"inverseBindMatrices":4,"joints":[1,2]}],"animations":[{"channels":[{"sampler":0,"target":{"node":2,"path":"rotation"}}],"samplers":[{"input":5,"interpolation":"STEP","output":6}]}],"buffers":[{"uri":"data:application/gltf-buffer;base64,AAABAAMAAAADAAIAAgADAAUAAgAFAAQABAAFAAcABAAHAAYABgAHAAkABgAJAAgAAAAAvwAAAAAAAAAAAAAAPwAAAAAAAAAAAAAAvwAAAD8AAAAAAAAAPwAAAD8AAAAAAAAAvwAAgD8AAAAAAAAAPwAAgD8AAAAAAAAAvwAAwD8AAAAAAAAAPwAAwD8AAAAAAAAAvwAAAEAAAAAAAAAAPwAAAEAAAAAA","byteLength":168},{"uri":"data:application/gltf-buffer;base64,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAABAPwAAgD4AAAAAAAAAAAAAQD8AAIA+AAAAAAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAPwAAAD8AAAAAAAAAAAAAgD4AAEA/AAAAAAAAAAAAAIA+AABAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAA=","byteLength":320},{"uri":"data:application/gltf-buffer;base64,AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8=","byteLength":128},{"uri":"data:application/gltf-buffer;base64,AAAAAAAAAD8AAIA/AADAPwAAAEAAACBAAABAQAAAYEAAAIBAAACQQAAAoEAAALBAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAPT9ND/0/TQ/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAPT9NL/0/TQ/AAAAAAAAAAD0/TS/9P00PwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAAAAAAAAAIA/","byteLength":240}],"bufferViews":[{"buffer":0,"byteLength":48,"target":34963},{"buffer":0,"byteOffset":48,"byteLength":120,"target":34962},{"buffer":1,"byteLength":320,"byteStride":16},{"buffer":2,"byteLength":128},{"buffer":3,"byteLength":240}],"accessors":[{"bufferView":0,"componentType":5123,"count":24,"type":"SCALAR"},{"bufferView":1,"componentType":5126,"count":10,"type":"VEC3","max":[0.5,2.0,0.0],"min":[-0.5,0.0,0.0]},{"bufferView":2,"componentType":5123,"count":10,"type":"VEC4"},{"bufferView":2,"byteOffset":160,"componentType":5126,"count":10,"type":"VEC4"},{"bufferView":3,"componentType":5126,"count":2,"type":"MAT4"},{"bufferView":4,"componentType":5126,"count":12,"type":"SCALAR","max":[5.5],"min":[0.0]},{"bufferView":4,"byteOffset":48,"componentType":5126,"count":12,"type":"VEC4","max":[0.0,0.0,0.707,1.0],"min":[0.0,0.0,-0.707,0.707]}],"asset":{"version":"2.0"}}

View file

@ -134,7 +134,7 @@ public:
//! Index buffer //! Index buffer
SIndexBuffer *Indices; SIndexBuffer *Indices;
//! Bounding box of this meshbuffer. //! Bounding box of this meshbuffer.
core::aabbox3d<f32> BoundingBox; core::aabbox3d<f32> BoundingBox{{0, 0, 0}};
//! Primitive type used for rendering (triangles, lines, ...) //! Primitive type used for rendering (triangles, lines, ...)
E_PRIMITIVE_TYPE PrimitiveType; E_PRIMITIVE_TYPE PrimitiveType;
}; };

View file

@ -1,85 +0,0 @@
#pragma once
#include "irrTypes.h"
namespace irr
{
namespace video
{
//! Compile target enumeration for the addHighLevelShaderMaterial() method.
enum E_VERTEX_SHADER_TYPE
{
EVST_VS_1_1 = 0,
EVST_VS_2_0,
EVST_VS_2_a,
EVST_VS_3_0,
EVST_VS_4_0,
EVST_VS_4_1,
EVST_VS_5_0,
//! This is not a type, but a value indicating how much types there are.
EVST_COUNT
};
//! Names for all vertex shader types, each entry corresponds to a E_VERTEX_SHADER_TYPE entry.
const c8 *const VERTEX_SHADER_TYPE_NAMES[] = {
"vs_1_1",
"vs_2_0",
"vs_2_a",
"vs_3_0",
"vs_4_0",
"vs_4_1",
"vs_5_0",
0};
//! Compile target enumeration for the addHighLevelShaderMaterial() method.
enum E_PIXEL_SHADER_TYPE
{
EPST_PS_1_1 = 0,
EPST_PS_1_2,
EPST_PS_1_3,
EPST_PS_1_4,
EPST_PS_2_0,
EPST_PS_2_a,
EPST_PS_2_b,
EPST_PS_3_0,
EPST_PS_4_0,
EPST_PS_4_1,
EPST_PS_5_0,
//! This is not a type, but a value indicating how much types there are.
EPST_COUNT
};
//! Names for all pixel shader types, each entry corresponds to a E_PIXEL_SHADER_TYPE entry.
const c8 *const PIXEL_SHADER_TYPE_NAMES[] = {
"ps_1_1",
"ps_1_2",
"ps_1_3",
"ps_1_4",
"ps_2_0",
"ps_2_a",
"ps_2_b",
"ps_3_0",
"ps_4_0",
"ps_4_1",
"ps_5_0",
0};
//! Enum for supported geometry shader types
enum E_GEOMETRY_SHADER_TYPE
{
EGST_GS_4_0 = 0,
//! This is not a type, but a value indicating how much types there are.
EGST_COUNT
};
//! String names for supported geometry shader types
const c8 *const GEOMETRY_SHADER_TYPE_NAMES[] = {
"gs_4_0",
0};
} // end namespace video
} // end namespace irr

View file

@ -4,7 +4,6 @@
#pragma once #pragma once
#include "EShaderTypes.h"
#include "EMaterialTypes.h" #include "EMaterialTypes.h"
#include "EPrimitiveTypes.h" #include "EPrimitiveTypes.h"
#include "path.h" #include "path.h"
@ -31,26 +30,15 @@ public:
virtual ~IGPUProgrammingServices() {} virtual ~IGPUProgrammingServices() {}
//! Adds a new high-level shading material renderer to the VideoDriver. //! Adds a new high-level shading material renderer to the VideoDriver.
/** Currently only HLSL/D3D9 and GLSL/OpenGL are supported. /**
\param vertexShaderProgram String containing the source of the vertex \param vertexShaderProgram String containing the source of the vertex
shader program. This can be 0 if no vertex program shall be used. shader program. This can be 0 if no vertex program shall be used.
\param vertexShaderEntryPointName Name of the entry function of the
vertexShaderProgram (p.e. "main")
\param vsCompileTarget Vertex shader version the high level shader
shall be compiled to.
\param pixelShaderProgram String containing the source of the pixel \param pixelShaderProgram String containing the source of the pixel
shader program. This can be 0 if no pixel shader shall be used. shader program. This can be 0 if no pixel shader shall be used.
\param pixelShaderEntryPointName Entry name of the function of the
pixelShaderProgram (p.e. "main")
\param psCompileTarget Pixel shader version the high level shader
shall be compiled to.
\param geometryShaderProgram String containing the source of the \param geometryShaderProgram String containing the source of the
geometry shader program. This can be 0 if no geometry shader shall be geometry shader program. This can be 0 if no geometry shader shall be
used. used.
\param geometryShaderEntryPointName Entry name of the function of the \param shaderName Name of the shader for debug purposes
geometryShaderProgram (p.e. "main")
\param gsCompileTarget Geometry shader version the high level shader
shall be compiled to.
\param inType Type of vertices passed to geometry shader \param inType Type of vertices passed to geometry shader
\param outType Type of vertices created by geometry shader \param outType Type of vertices created by geometry shader
\param verticesOut Maximal number of vertices created by geometry \param verticesOut Maximal number of vertices created by geometry
@ -73,108 +61,43 @@ public:
error log and can be caught with a custom event receiver. */ error log and can be caught with a custom event receiver. */
virtual s32 addHighLevelShaderMaterial( virtual s32 addHighLevelShaderMaterial(
const c8 *vertexShaderProgram, const c8 *vertexShaderProgram,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8 *pixelShaderProgram, const c8 *pixelShaderProgram,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const c8 *geometryShaderProgram, const c8 *geometryShaderProgram,
const c8 *geometryShaderEntryPointName = "main", const c8 *shaderName = nullptr,
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES, scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0, u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0, IShaderConstantSetCallBack *callback = nullptr,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID, E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0) = 0; s32 userData = 0) = 0;
//! convenience function for use without geometry shaders //! convenience function for use without geometry shaders
s32 addHighLevelShaderMaterial( s32 addHighLevelShaderMaterial(
const c8 *vertexShaderProgram, const c8 *vertexShaderProgram,
const c8 *vertexShaderEntryPointName = "main", const c8 *pixelShaderProgram = nullptr,
E_VERTEX_SHADER_TYPE vsCompileTarget = EVST_VS_1_1, const c8 *shaderName = nullptr,
const c8 *pixelShaderProgram = 0, IShaderConstantSetCallBack *callback = nullptr,
const c8 *pixelShaderEntryPointName = "main",
E_PIXEL_SHADER_TYPE psCompileTarget = EPST_PS_1_1,
IShaderConstantSetCallBack *callback = 0,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID, E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0) s32 userData = 0)
{ {
return addHighLevelShaderMaterial( return addHighLevelShaderMaterial(
vertexShaderProgram, vertexShaderEntryPointName, vertexShaderProgram, pixelShaderProgram,
vsCompileTarget, pixelShaderProgram, nullptr, shaderName,
pixelShaderEntryPointName, psCompileTarget,
0, "main", EGST_GS_4_0,
scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0,
callback, baseMaterial, userData); callback, baseMaterial, userData);
} }
//! convenience function for use with many defaults, without geometry shader
/** All shader names are set to "main" and compile targets are shader
type 1.1.
*/
s32 addHighLevelShaderMaterial(
const c8 *vertexShaderProgram,
const c8 *pixelShaderProgram = 0,
IShaderConstantSetCallBack *callback = 0,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0)
{
return addHighLevelShaderMaterial(
vertexShaderProgram, "main",
EVST_VS_1_1, pixelShaderProgram,
"main", EPST_PS_1_1,
0, "main", EGST_GS_4_0,
scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0,
callback, baseMaterial, userData);
}
//! convenience function for use with many defaults, with geometry shader
/** All shader names are set to "main" and compile targets are shader
type 1.1 and geometry shader 4.0.
*/
s32 addHighLevelShaderMaterial(
const c8 *vertexShaderProgram,
const c8 *pixelShaderProgram = 0,
const c8 *geometryShaderProgram = 0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0)
{
return addHighLevelShaderMaterial(
vertexShaderProgram, "main",
EVST_VS_1_1, pixelShaderProgram,
"main", EPST_PS_1_1,
geometryShaderProgram, "main", EGST_GS_4_0,
inType, outType, verticesOut,
callback, baseMaterial, userData);
}
//! Like addHighLevelShaderMaterial(), but loads from files. //! Like addHighLevelShaderMaterial(), but loads from files.
/** \param vertexShaderProgramFileName Text file containing the source /** \param vertexShaderProgramFileName Text file containing the source
of the vertex shader program. Set to empty string if no vertex shader of the vertex shader program. Set to empty string if no vertex shader
shall be created. shall be created.
\param vertexShaderEntryPointName Name of the entry function of the
vertexShaderProgram (p.e. "main")
\param vsCompileTarget Vertex shader version the high level shader
shall be compiled to.
\param pixelShaderProgramFileName Text file containing the source of \param pixelShaderProgramFileName Text file containing the source of
the pixel shader program. Set to empty string if no pixel shader shall the pixel shader program. Set to empty string if no pixel shader shall
be created. be created.
\param pixelShaderEntryPointName Entry name of the function of the
pixelShaderProgram (p.e. "main")
\param psCompileTarget Pixel shader version the high level shader
shall be compiled to.
\param geometryShaderProgramFileName Name of the source of \param geometryShaderProgramFileName Name of the source of
the geometry shader program. Set to empty string if no geometry shader the geometry shader program. Set to empty string if no geometry shader
shall be created. shall be created.
\param geometryShaderEntryPointName Entry name of the function of the \param shaderName Name of the shader for debug purposes
geometryShaderProgram (p.e. "main")
\param gsCompileTarget Geometry shader version the high level shader
shall be compiled to.
\param inType Type of vertices passed to geometry shader \param inType Type of vertices passed to geometry shader
\param outType Type of vertices created by geometry shader \param outType Type of vertices created by geometry shader
\param verticesOut Maximal number of vertices created by geometry \param verticesOut Maximal number of vertices created by geometry
@ -197,164 +120,16 @@ public:
error log and can be caught with a custom event receiver. */ error log and can be caught with a custom event receiver. */
virtual s32 addHighLevelShaderMaterialFromFiles( virtual s32 addHighLevelShaderMaterialFromFiles(
const io::path &vertexShaderProgramFileName, const io::path &vertexShaderProgramFileName,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const io::path &pixelShaderProgramFileName, const io::path &pixelShaderProgramFileName,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const io::path &geometryShaderProgramFileName, const io::path &geometryShaderProgramFileName,
const c8 *geometryShaderEntryPointName = "main", const c8 *shaderName = nullptr,
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES, scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0, u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0, IShaderConstantSetCallBack *callback = nullptr,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID, E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0) = 0; s32 userData = 0) = 0;
//! convenience function for use without geometry shaders
s32 addHighLevelShaderMaterialFromFiles(
const io::path &vertexShaderProgramFileName,
const c8 *vertexShaderEntryPointName = "main",
E_VERTEX_SHADER_TYPE vsCompileTarget = EVST_VS_1_1,
const io::path &pixelShaderProgramFileName = "",
const c8 *pixelShaderEntryPointName = "main",
E_PIXEL_SHADER_TYPE psCompileTarget = EPST_PS_1_1,
IShaderConstantSetCallBack *callback = 0,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0)
{
return addHighLevelShaderMaterialFromFiles(
vertexShaderProgramFileName, vertexShaderEntryPointName,
vsCompileTarget, pixelShaderProgramFileName,
pixelShaderEntryPointName, psCompileTarget,
"", "main", EGST_GS_4_0,
scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0,
callback, baseMaterial, userData);
}
//! convenience function for use with many defaults, without geometry shader
/** All shader names are set to "main" and compile targets are shader
type 1.1.
*/
s32 addHighLevelShaderMaterialFromFiles(
const io::path &vertexShaderProgramFileName,
const io::path &pixelShaderProgramFileName = "",
IShaderConstantSetCallBack *callback = 0,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0)
{
return addHighLevelShaderMaterialFromFiles(
vertexShaderProgramFileName, "main",
EVST_VS_1_1, pixelShaderProgramFileName,
"main", EPST_PS_1_1,
"", "main", EGST_GS_4_0,
scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0,
callback, baseMaterial, userData);
}
//! convenience function for use with many defaults, with geometry shader
/** All shader names are set to "main" and compile targets are shader
type 1.1 and geometry shader 4.0.
*/
s32 addHighLevelShaderMaterialFromFiles(
const io::path &vertexShaderProgramFileName,
const io::path &pixelShaderProgramFileName = "",
const io::path &geometryShaderProgramFileName = "",
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0)
{
return addHighLevelShaderMaterialFromFiles(
vertexShaderProgramFileName, "main",
EVST_VS_1_1, pixelShaderProgramFileName,
"main", EPST_PS_1_1,
geometryShaderProgramFileName, "main", EGST_GS_4_0,
inType, outType, verticesOut,
callback, baseMaterial, userData);
}
//! Like addHighLevelShaderMaterial(), but loads from files.
/** \param vertexShaderProgram Text file handle containing the source
of the vertex shader program. Set to 0 if no vertex shader shall be
created.
\param vertexShaderEntryPointName Name of the entry function of the
vertexShaderProgram
\param vsCompileTarget Vertex shader version the high level shader
shall be compiled to.
\param pixelShaderProgram Text file handle containing the source of
the pixel shader program. Set to 0 if no pixel shader shall be created.
\param pixelShaderEntryPointName Entry name of the function of the
pixelShaderProgram (p.e. "main")
\param psCompileTarget Pixel shader version the high level shader
shall be compiled to.
\param geometryShaderProgram Text file handle containing the source of
the geometry shader program. Set to 0 if no geometry shader shall be
created.
\param geometryShaderEntryPointName Entry name of the function of the
geometryShaderProgram (p.e. "main")
\param gsCompileTarget Geometry shader version the high level shader
shall be compiled to.
\param inType Type of vertices passed to geometry shader
\param outType Type of vertices created by geometry shader
\param verticesOut Maximal number of vertices created by geometry
shader. If 0, maximal number supported is assumed.
\param callback Pointer to an implementation of
IShaderConstantSetCallBack in which you can set the needed vertex and
pixel shader program constants. Set this to 0 if you don't need this.
\param baseMaterial Base material which renderstates will be used to
shade the material.
\param userData a user data int. This int can be set to any value and
will be set as parameter in the callback method when calling
OnSetConstants(). In this way it is easily possible to use the same
callback method for multiple materials and distinguish between them
during the call.
\return Number of the material type which can be set in
SMaterial::MaterialType to use the renderer. -1 is returned if an
error occurred, e.g. if a shader program could not be compiled or a
compile target is not reachable. The error strings are then printed to
the error log and can be caught with a custom event receiver. */
virtual s32 addHighLevelShaderMaterialFromFiles(
io::IReadFile *vertexShaderProgram,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
io::IReadFile *pixelShaderProgram,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
io::IReadFile *geometryShaderProgram,
const c8 *geometryShaderEntryPointName = "main",
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0) = 0;
//! convenience function for use without geometry shaders
s32 addHighLevelShaderMaterialFromFiles(
io::IReadFile *vertexShaderProgram,
const c8 *vertexShaderEntryPointName = "main",
E_VERTEX_SHADER_TYPE vsCompileTarget = EVST_VS_1_1,
io::IReadFile *pixelShaderProgram = 0,
const c8 *pixelShaderEntryPointName = "main",
E_PIXEL_SHADER_TYPE psCompileTarget = EPST_PS_1_1,
IShaderConstantSetCallBack *callback = 0,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0)
{
return addHighLevelShaderMaterialFromFiles(
vertexShaderProgram, vertexShaderEntryPointName,
vsCompileTarget, pixelShaderProgram,
pixelShaderEntryPointName, psCompileTarget,
0, "main", EGST_GS_4_0,
scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0,
callback, baseMaterial, userData);
}
//! Delete a shader material and associated data. //! Delete a shader material and associated data.
/** /**
After you have deleted a material it is invalid to still use and doing After you have deleted a material it is invalid to still use and doing

View file

@ -53,7 +53,7 @@ public:
//! Calculates the width and height of a given string of text. //! Calculates the width and height of a given string of text.
/** \return Returns width and height of the area covered by the text if /** \return Returns width and height of the area covered by the text if
it would be drawn. */ it would be drawn. */
virtual core::dimension2d<u32> getDimension(const wchar_t *text) const = 0; virtual core::dimension2du getDimension(const wchar_t *text) const = 0;
//! Calculates the index of the character in the text which is on a specific position. //! Calculates the index of the character in the text which is on a specific position.
/** \param text: Text string. /** \param text: Text string.
@ -82,10 +82,7 @@ public:
which supports kerning pairs a string such as 'Wo' may have the 'o' which supports kerning pairs a string such as 'Wo' may have the 'o'
tucked neatly under the 'W'. tucked neatly under the 'W'.
*/ */
virtual s32 getKerningWidth(const wchar_t *thisLetter = 0, const wchar_t *previousLetter = 0) const = 0; virtual core::vector2di getKerning(const wchar_t thisLetter = 0, const wchar_t previousLetter = 0) const = 0;
//! Returns the distance between letters
virtual s32 getKerningHeight() const = 0;
//! Define which characters should not be drawn by the font. //! Define which characters should not be drawn by the font.
/** For example " " would not draw any space which is usually blank in /** For example " " would not draw any space which is usually blank in

View file

@ -24,17 +24,6 @@ public:
//! returns the sprite number from a given character //! returns the sprite number from a given character
virtual u32 getSpriteNoFromChar(const wchar_t *c) const = 0; virtual u32 getSpriteNoFromChar(const wchar_t *c) const = 0;
//! Gets kerning values (distance between letters) for the font. If no parameters are provided,
/** the global kerning distance is returned.
\param thisLetter: If this parameter is provided, the left side kerning for this letter is added
to the global kerning value. For example, a space might only be one pixel wide, but it may
be displayed as several pixels.
\param previousLetter: If provided, kerning is calculated for both letters and added to the global
kerning value. For example, EGFT_BITMAP will add the right kerning value of previousLetter to the
left side kerning value of thisLetter, then add the global value.
*/
s32 getKerningWidth(const wchar_t *thisLetter = 0, const wchar_t *previousLetter = 0) const override = 0;
}; };
} // end namespace gui } // end namespace gui

View file

@ -108,7 +108,7 @@ public:
if (!mesh) if (!mesh)
return true; return true;
bool result = true; bool result = true;
core::aabbox3df bufferbox; core::aabbox3df bufferbox{{0, 0, 0}};
for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) { for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) {
result &= apply(func, mesh->getMeshBuffer(i), boundingBoxUpdate); result &= apply(func, mesh->getMeshBuffer(i), boundingBoxUpdate);
if (boundingBoxUpdate) { if (boundingBoxUpdate) {
@ -136,7 +136,7 @@ protected:
if (!buffer) if (!buffer)
return true; return true;
core::aabbox3df bufferbox; core::aabbox3df bufferbox{{0, 0, 0}};
for (u32 i = 0; i < buffer->getVertexCount(); ++i) { for (u32 i = 0; i < buffer->getVertexCount(); ++i) {
switch (buffer->getVertexType()) { switch (buffer->getVertexType()) {
case video::EVT_STANDARD: { case video::EVT_STANDARD: {

View file

@ -61,8 +61,8 @@ struct SFrameStats {
u32 PrimitivesDrawn = 0; u32 PrimitivesDrawn = 0;
//! Number of hardware buffers uploaded (new or updated) //! Number of hardware buffers uploaded (new or updated)
u32 HWBuffersUploaded = 0; u32 HWBuffersUploaded = 0;
//! Sum of uploaded hardware buffer size //! Number of active hardware buffers
u32 HWBuffersUploadedSize = 0; u32 HWBuffersActive = 0;
}; };
//! Interface to driver which is able to perform 2d and 3d graphics functions. //! Interface to driver which is able to perform 2d and 3d graphics functions.
@ -310,6 +310,18 @@ public:
0 or another texture first. */ 0 or another texture first. */
virtual void removeAllTextures() = 0; virtual void removeAllTextures() = 0;
//! Eagerly upload buffer to hardware
/** This can be a good idea if you have a newly created or modified buffer,
which you know you will draw in the near future (e.g. end of same frame,
or next frame), because it gives the GPU driver to copy the contents. */
virtual void updateHardwareBuffer(const scene::IVertexBuffer *vb) = 0;
//! Eagerly upload buffer to hardware
/** This can be a good idea if you have a newly created or modified buffer,
which you know you will draw in the near future (e.g. end of same frame,
or next frame), because it gives the GPU driver to copy the contents. */
virtual void updateHardwareBuffer(const scene::IIndexBuffer *ib) = 0;
//! Remove hardware buffer //! Remove hardware buffer
virtual void removeHardwareBuffer(const scene::IVertexBuffer *vb) = 0; virtual void removeHardwareBuffer(const scene::IVertexBuffer *vb) = 0;

View file

@ -154,7 +154,7 @@ struct SAnimatedMesh final : public IAnimatedMesh
std::vector<IMesh *> Meshes; std::vector<IMesh *> Meshes;
//! The bounding box of this mesh //! The bounding box of this mesh
core::aabbox3d<f32> Box; core::aabbox3d<f32> Box{{0.0f, 0.0f, 0.0f}};
//! Default animation speed of this mesh. //! Default animation speed of this mesh.
f32 FramesPerSecond; f32 FramesPerSecond;

View file

@ -133,7 +133,7 @@ struct SMesh final : public IMesh
std::vector<u32> TextureSlots; std::vector<u32> TextureSlots;
//! The bounding box of this mesh //! The bounding box of this mesh
core::aabbox3d<f32> BoundingBox; core::aabbox3d<f32> BoundingBox{{0, 0, 0}};
}; };
} // end namespace scene } // end namespace scene

View file

@ -228,7 +228,7 @@ public:
video::SMaterial Material; video::SMaterial Material;
video::E_VERTEX_TYPE VertexType; video::E_VERTEX_TYPE VertexType;
core::aabbox3d<f32> BoundingBox; core::aabbox3d<f32> BoundingBox{{0, 0, 0}};
//! Primitive type used for rendering (triangles, lines, ...) //! Primitive type used for rendering (triangles, lines, ...)
E_PRIMITIVE_TYPE PrimitiveType; E_PRIMITIVE_TYPE PrimitiveType;

View file

@ -117,7 +117,7 @@ struct SViewFrustum
core::plane3d<f32> planes[VF_PLANE_COUNT]; core::plane3d<f32> planes[VF_PLANE_COUNT];
//! bounding box around the view frustum //! bounding box around the view frustum
core::aabbox3d<f32> boundingBox; core::aabbox3d<f32> boundingBox{{0, 0, 0}};
private: private:
//! Hold a copy of important transform matrices //! Hold a copy of important transform matrices

View file

@ -137,7 +137,7 @@ public:
//! Moves the mesh into static position. //! Moves the mesh into static position.
void resetAnimation(); void resetAnimation();
virtual void updateBoundingBox(); void updateBoundingBox();
//! Recovers the joints from the mesh //! Recovers the joints from the mesh
void recoverJointsFromMesh(std::vector<IBoneSceneNode *> &jointChildSceneNodes); void recoverJointsFromMesh(std::vector<IBoneSceneNode *> &jointChildSceneNodes);
@ -370,7 +370,7 @@ protected:
// doesn't allow taking a reference to individual elements. // doesn't allow taking a reference to individual elements.
std::vector<std::vector<char>> Vertices_Moved; std::vector<std::vector<char>> Vertices_Moved;
core::aabbox3d<f32> BoundingBox; core::aabbox3d<f32> BoundingBox{{0, 0, 0}};
f32 EndFrame; f32 EndFrame;
f32 FramesPerSecond; f32 FramesPerSecond;

View file

@ -20,9 +20,7 @@ template <class T>
class aabbox3d class aabbox3d
{ {
public: public:
//! Default Constructor. constexpr aabbox3d() = delete;
constexpr aabbox3d() :
MinEdge(-1, -1, -1), MaxEdge(1, 1, 1) {}
//! Constructor with min edge and max edge. //! Constructor with min edge and max edge.
constexpr aabbox3d(const vector3d<T> &min, const vector3d<T> &max) : constexpr aabbox3d(const vector3d<T> &min, const vector3d<T> &max) :
MinEdge(min), MaxEdge(max) {} MinEdge(min), MaxEdge(max) {}

View file

@ -145,7 +145,7 @@ private:
void beginTransition(); void beginTransition();
core::array<video::SMaterial> Materials; core::array<video::SMaterial> Materials;
core::aabbox3d<f32> Box; core::aabbox3d<f32> Box{{0.0f, 0.0f, 0.0f}};
IAnimatedMesh *Mesh; IAnimatedMesh *Mesh;
f32 StartFrame; f32 StartFrame;

View file

@ -104,7 +104,7 @@ private:
/** Note that we can't use the real boundingbox for culling because at that point /** Note that we can't use the real boundingbox for culling because at that point
the camera which is used to calculate the billboard is not yet updated. So we only the camera which is used to calculate the billboard is not yet updated. So we only
know the real boundingbox after rendering - which is too late for culling. */ know the real boundingbox after rendering - which is too late for culling. */
core::aabbox3d<f32> BBoxSafe; core::aabbox3d<f32> BBoxSafe{{0.0f, 0.0f, 0.0f}};
scene::SMeshBuffer *Buffer; scene::SMeshBuffer *Buffer;
}; };

View file

@ -60,7 +60,7 @@ private:
u32 BoneIndex; u32 BoneIndex;
core::aabbox3d<f32> Box; core::aabbox3d<f32> Box{-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
E_BONE_ANIMATION_MODE AnimationMode; E_BONE_ANIMATION_MODE AnimationMode;
E_BONE_SKINNING_SPACE SkinningSpace; E_BONE_SKINNING_SPACE SkinningSpace;

View file

@ -48,7 +48,7 @@ private:
void setPosition(const core::vector3df &newpos) override; void setPosition(const core::vector3df &newpos) override;
core::matrix4 RelativeTransformationMatrix; core::matrix4 RelativeTransformationMatrix;
core::aabbox3d<f32> Box; core::aabbox3d<f32> Box{{0, 0, 0}};
}; };
} // end namespace scene } // end namespace scene

View file

@ -33,7 +33,7 @@ public:
ISceneNode *clone(ISceneNode *newParent = 0, ISceneManager *newManager = 0) override; ISceneNode *clone(ISceneNode *newParent = 0, ISceneManager *newManager = 0) override;
private: private:
core::aabbox3d<f32> Box; core::aabbox3d<f32> Box{{0, 0, 0}};
}; };
} // end namespace scene } // end namespace scene

View file

@ -677,8 +677,17 @@ void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx)
for (const auto &channel : anim.channels) { for (const auto &channel : anim.channels) {
const auto &sampler = anim.samplers.at(channel.sampler); const auto &sampler = anim.samplers.at(channel.sampler);
if (sampler.interpolation != tiniergltf::AnimationSampler::Interpolation::LINEAR)
throw std::runtime_error("unsupported interpolation, only linear interpolation is supported"); bool interpolate = ([&]() {
switch (sampler.interpolation) {
case tiniergltf::AnimationSampler::Interpolation::STEP:
return false;
case tiniergltf::AnimationSampler::Interpolation::LINEAR:
return true;
default:
throw std::runtime_error("Only STEP and LINEAR keyframe interpolation are supported");
}
})();
const auto inputAccessor = Accessor<f32>::make(m_gltf_model, sampler.input); const auto inputAccessor = Accessor<f32>::make(m_gltf_model, sampler.input);
const auto n_frames = inputAccessor.getCount(); const auto n_frames = inputAccessor.getCount();
@ -686,32 +695,38 @@ void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx)
if (!channel.target.node.has_value()) if (!channel.target.node.has_value())
throw std::runtime_error("no animated node"); throw std::runtime_error("no animated node");
const auto &joint = m_loaded_nodes.at(*channel.target.node); auto *joint = m_loaded_nodes.at(*channel.target.node);
switch (channel.target.path) { switch (channel.target.path) {
case tiniergltf::AnimationChannelTarget::Path::TRANSLATION: { case tiniergltf::AnimationChannelTarget::Path::TRANSLATION: {
const auto outputAccessor = Accessor<core::vector3df>::make(m_gltf_model, sampler.output); const auto outputAccessor = Accessor<core::vector3df>::make(m_gltf_model, sampler.output);
auto &channel = joint->keys.position;
channel.interpolate = interpolate;
for (std::size_t i = 0; i < n_frames; ++i) { for (std::size_t i = 0; i < n_frames; ++i) {
f32 frame = inputAccessor.get(i); f32 frame = inputAccessor.get(i);
core::vector3df position = outputAccessor.get(i); core::vector3df position = outputAccessor.get(i);
m_irr_model->addPositionKey(joint, frame, convertHandedness(position)); channel.pushBack(frame, convertHandedness(position));
} }
break; break;
} }
case tiniergltf::AnimationChannelTarget::Path::ROTATION: { case tiniergltf::AnimationChannelTarget::Path::ROTATION: {
const auto outputAccessor = Accessor<core::quaternion>::make(m_gltf_model, sampler.output); const auto outputAccessor = Accessor<core::quaternion>::make(m_gltf_model, sampler.output);
auto &channel = joint->keys.rotation;
channel.interpolate = interpolate;
for (std::size_t i = 0; i < n_frames; ++i) { for (std::size_t i = 0; i < n_frames; ++i) {
f32 frame = inputAccessor.get(i); f32 frame = inputAccessor.get(i);
core::quaternion rotation = outputAccessor.get(i); core::quaternion rotation = outputAccessor.get(i);
m_irr_model->addRotationKey(joint, frame, convertHandedness(rotation)); channel.pushBack(frame, convertHandedness(rotation));
} }
break; break;
} }
case tiniergltf::AnimationChannelTarget::Path::SCALE: { case tiniergltf::AnimationChannelTarget::Path::SCALE: {
const auto outputAccessor = Accessor<core::vector3df>::make(m_gltf_model, sampler.output); const auto outputAccessor = Accessor<core::vector3df>::make(m_gltf_model, sampler.output);
auto &channel = joint->keys.scale;
channel.interpolate = interpolate;
for (std::size_t i = 0; i < n_frames; ++i) { for (std::size_t i = 0; i < n_frames; ++i) {
f32 frame = inputAccessor.get(i); f32 frame = inputAccessor.get(i);
core::vector3df scale = outputAccessor.get(i); core::vector3df scale = outputAccessor.get(i);
m_irr_model->addScaleKey(joint, frame, scale); channel.pushBack(frame, scale);
} }
break; break;
} }

View file

@ -788,9 +788,9 @@ void CGUIEditBox::draw()
mbegin = font->getDimension(s.c_str()).Width; mbegin = font->getDimension(s.c_str()).Width;
// deal with kerning // deal with kerning
mbegin += font->getKerningWidth( mbegin += font->getKerning(
&((*txtLine)[realmbgn - startPos]), (*txtLine)[realmbgn - startPos],
realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0); realmbgn - startPos > 0 ? (*txtLine)[realmbgn - startPos - 1] : 0).X;
lineStartPos = realmbgn - startPos; lineStartPos = realmbgn - startPos;
} }
@ -832,7 +832,8 @@ void CGUIEditBox::draw()
} }
s = txtLine->subString(0, CursorPos - startPos); s = txtLine->subString(0, CursorPos - startPos);
charcursorpos = font->getDimension(s.c_str()).Width + charcursorpos = font->getDimension(s.c_str()).Width +
font->getKerningWidth(CursorChar.c_str(), CursorPos - startPos > 0 ? &((*txtLine)[CursorPos - startPos - 1]) : 0); font->getKerning(CursorChar[0],
CursorPos - startPos > 0 ? (*txtLine)[CursorPos - startPos - 1] : 0).X;
if (focus && (CursorBlinkTime == 0 || (os::Timer::getTime() - BlinkStartTime) % (2 * CursorBlinkTime) < CursorBlinkTime)) { if (focus && (CursorBlinkTime == 0 || (os::Timer::getTime() - BlinkStartTime) % (2 * CursorBlinkTime) < CursorBlinkTime)) {
setTextRect(cursorLine); setTextRect(cursorLine);
@ -1194,7 +1195,7 @@ void CGUIEditBox::setTextRect(s32 line)
d = font->getDimension(Text.c_str()); d = font->getDimension(Text.c_str());
d.Height = AbsoluteRect.getHeight(); d.Height = AbsoluteRect.getHeight();
} }
d.Height += font->getKerningHeight(); d.Height += font->getKerning(L'A').Y;
// justification // justification
switch (HAlign) { switch (HAlign) {
@ -1382,7 +1383,7 @@ void CGUIEditBox::calculateScrollPos()
// calculate vertical scrolling // calculate vertical scrolling
if (hasBrokenText) { if (hasBrokenText) {
irr::u32 lineHeight = font->getDimension(L"A").Height + font->getKerningHeight(); irr::u32 lineHeight = font->getDimension(L"A").Height + font->getKerning(L'A').Y;
// only up to 1 line fits? // only up to 1 line fits?
if (lineHeight >= (irr::u32)FrameRect.getHeight()) { if (lineHeight >= (irr::u32)FrameRect.getHeight()) {
VScrollPos = 0; VScrollPos = 0;

View file

@ -53,141 +53,6 @@ CGUIFont::~CGUIFont()
} }
} }
#if 0
//! loads a font file from xml
bool CGUIFont::load(io::IXMLReader* xml, const io::path& directory)
{
if (!SpriteBank)
return false;
SpriteBank->clear();
while (xml->read())
{
if (io::EXN_ELEMENT == xml->getNodeType())
{
if (core::stringw(L"Texture") == xml->getNodeName())
{
// add a texture
core::stringc fn = xml->getAttributeValue(L"filename");
u32 i = (u32)xml->getAttributeValueAsInt(L"index");
core::stringw alpha = xml->getAttributeValue(L"hasAlpha");
while (i+1 > SpriteBank->getTextureCount())
SpriteBank->addTexture(0);
bool flags[3];
pushTextureCreationFlags(flags);
// load texture
io::path textureFullName = core::mergeFilename(directory, fn);
SpriteBank->setTexture(i, Driver->getTexture(textureFullName));
popTextureCreationFlags(flags);
// couldn't load texture, abort.
if (!SpriteBank->getTexture(i))
{
os::Printer::log("Unable to load all textures in the font, aborting", ELL_ERROR);
return false;
}
else
{
// colorkey texture rather than alpha channel?
if (alpha == core::stringw("false"))
Driver->makeColorKeyTexture(SpriteBank->getTexture(i), core::position2di(0,0));
}
}
else if (core::stringw(L"c") == xml->getNodeName())
{
// adding a character to this font
SFontArea a;
SGUISpriteFrame f;
SGUISprite s;
core::rect<s32> rectangle;
a.underhang = xml->getAttributeValueAsInt(L"u");
a.overhang = xml->getAttributeValueAsInt(L"o");
a.spriteno = SpriteBank->getSprites().size();
s32 texno = xml->getAttributeValueAsInt(L"i");
// parse rectangle
core::stringc rectstr = xml->getAttributeValue(L"r");
wchar_t ch = xml->getAttributeValue(L"c")[0];
const c8 *c = rectstr.c_str();
s32 val;
val = 0;
while (*c >= '0' && *c <= '9')
{
val *= 10;
val += *c - '0';
c++;
}
rectangle.UpperLeftCorner.X = val;
while (*c == L' ' || *c == L',') c++;
val = 0;
while (*c >= '0' && *c <= '9')
{
val *= 10;
val += *c - '0';
c++;
}
rectangle.UpperLeftCorner.Y = val;
while (*c == L' ' || *c == L',') c++;
val = 0;
while (*c >= '0' && *c <= '9')
{
val *= 10;
val += *c - '0';
c++;
}
rectangle.LowerRightCorner.X = val;
while (*c == L' ' || *c == L',') c++;
val = 0;
while (*c >= '0' && *c <= '9')
{
val *= 10;
val += *c - '0';
c++;
}
rectangle.LowerRightCorner.Y = val;
CharacterMap.emplace(ch, Areas.size());
// make frame
f.rectNumber = SpriteBank->getPositions().size();
f.textureNumber = texno;
// add frame to sprite
s.Frames.push_back(f);
s.frameTime = 0;
// add rectangle to sprite bank
SpriteBank->getPositions().push_back(rectangle);
a.width = rectangle.getWidth();
// add sprite to sprite bank
SpriteBank->getSprites().push_back(s);
// add character to font
Areas.push_back(a);
}
}
}
// set bad character
WrongCharacter = getAreaFromCharacter(L' ');
setMaxHeight();
return true;
}
#endif
void CGUIFont::setMaxHeight() void CGUIFont::setMaxHeight()
{ {
if (!SpriteBank) if (!SpriteBank)
@ -365,17 +230,15 @@ void CGUIFont::setKerningWidth(s32 kerning)
GlobalKerningWidth = kerning; GlobalKerningWidth = kerning;
} }
//! set an Pixel Offset on Drawing ( scale position on width ) core::vector2di CGUIFont::getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const
s32 CGUIFont::getKerningWidth(const wchar_t *thisLetter, const wchar_t *previousLetter) const
{ {
s32 ret = GlobalKerningWidth; core::vector2di ret(GlobalKerningWidth, GlobalKerningHeight);
if (thisLetter) { if (thisLetter) {
ret += Areas[getAreaFromCharacter(*thisLetter)].overhang; ret.X += Areas[getAreaFromCharacter(thisLetter)].overhang;
if (previousLetter) { if (previousLetter)
ret += Areas[getAreaFromCharacter(*previousLetter)].underhang; ret.X += Areas[getAreaFromCharacter(previousLetter)].underhang;
}
} }
return ret; return ret;
@ -387,12 +250,6 @@ void CGUIFont::setKerningHeight(s32 kerning)
GlobalKerningHeight = kerning; GlobalKerningHeight = kerning;
} }
//! set an Pixel Offset on Drawing ( scale position on height )
s32 CGUIFont::getKerningHeight() const
{
return GlobalKerningHeight;
}
//! returns the sprite number from a given character //! returns the sprite number from a given character
u32 CGUIFont::getSpriteNoFromChar(const wchar_t *c) const u32 CGUIFont::getSpriteNoFromChar(const wchar_t *c) const
{ {

View file

@ -58,8 +58,7 @@ public:
void setKerningHeight(s32 kerning) override; void setKerningHeight(s32 kerning) override;
//! set an Pixel Offset on Drawing ( scale position on width ) //! set an Pixel Offset on Drawing ( scale position on width )
s32 getKerningWidth(const wchar_t *thisLetter = 0, const wchar_t *previousLetter = 0) const override; core::vector2di getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const override;
s32 getKerningHeight() const override;
//! gets the sprite bank //! gets the sprite bank
IGUISpriteBank *getSpriteBank() const override; IGUISpriteBank *getSpriteBank() const override;

View file

@ -74,10 +74,12 @@ void CGUIStaticText::draw()
IGUIFont *font = getActiveFont(); IGUIFont *font = getActiveFont();
if (font) { if (font) {
s32 kerningHeight = font->getKerning(L'A').Y;
if (!WordWrap) { if (!WordWrap) {
if (VAlign == EGUIA_LOWERRIGHT) { if (VAlign == EGUIA_LOWERRIGHT) {
frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y -
font->getDimension(L"A").Height - font->getKerningHeight(); font->getDimension(L"A").Height - kerningHeight;
} }
if (HAlign == EGUIA_LOWERRIGHT) { if (HAlign == EGUIA_LOWERRIGHT) {
frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X - frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
@ -92,7 +94,7 @@ void CGUIStaticText::draw()
breakText(); breakText();
core::rect<s32> r = frameRect; core::rect<s32> r = frameRect;
s32 height = font->getDimension(L"A").Height + font->getKerningHeight(); s32 height = font->getDimension(L"A").Height + kerningHeight;
s32 totalHeight = height * BrokenText.size(); s32 totalHeight = height * BrokenText.size();
if (VAlign == EGUIA_CENTER) { if (VAlign == EGUIA_CENTER) {
r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2); r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
@ -471,7 +473,7 @@ s32 CGUIStaticText::getTextHeight() const
return 0; return 0;
if (WordWrap) { if (WordWrap) {
s32 height = font->getDimension(L"A").Height + font->getKerningHeight(); s32 height = font->getDimension(L"A").Height + font->getKerning(L'A').Y;
return height * BrokenText.size(); return height * BrokenText.size();
} else { } else {
// TODO: Text can have multiple lines which are not in BrokenText // TODO: Text can have multiple lines which are not in BrokenText

View file

@ -348,6 +348,7 @@ if(ENABLE_OPENGL3 OR ENABLE_GLES2)
OpenGL/FixedPipelineRenderer.cpp OpenGL/FixedPipelineRenderer.cpp
OpenGL/MaterialRenderer.cpp OpenGL/MaterialRenderer.cpp
OpenGL/Renderer2D.cpp OpenGL/Renderer2D.cpp
OpenGL/VBO.cpp
) )
endif() endif()

View file

@ -72,7 +72,7 @@ protected:
void copyMaterials(); void copyMaterials();
core::array<video::SMaterial> Materials; core::array<video::SMaterial> Materials;
core::aabbox3d<f32> Box; core::aabbox3d<f32> Box{{0, 0, 0}};
video::SMaterial ReadOnlyMaterial; video::SMaterial ReadOnlyMaterial;
IMesh *Mesh; IMesh *Mesh;

View file

@ -218,7 +218,7 @@ bool CNullDriver::beginScene(u16 clearFlag, SColor clearColor, f32 clearDepth, u
bool CNullDriver::endScene() bool CNullDriver::endScene()
{ {
FPSCounter.registerFrame(os::Timer::getRealTime()); FPSCounter.registerFrame(os::Timer::getRealTime());
updateAllHardwareBuffers(); expireHardwareBuffers();
updateAllOcclusionQueries(); updateAllOcclusionQueries();
return true; return true;
} }
@ -1141,32 +1141,68 @@ CNullDriver::SHWBufferLink *CNullDriver::getBufferLink(const scene::IIndexBuffer
return createHardwareBuffer(ib); // no hardware links, and mesh wants one, create it return createHardwareBuffer(ib); // no hardware links, and mesh wants one, create it
} }
//! Update all hardware buffers, remove unused ones void CNullDriver::registerHardwareBuffer(SHWBufferLink *HWBuffer)
void CNullDriver::updateAllHardwareBuffers()
{ {
auto it = HWBufferList.begin(); _IRR_DEBUG_BREAK_IF(!HWBuffer)
while (it != HWBufferList.end()) { HWBuffer->ListPosition = HWBufferList.size();
SHWBufferLink *Link = *it; HWBufferList.push_back(HWBuffer);
++it; }
if (Link->IsVertex) { void CNullDriver::expireHardwareBuffers()
if (!Link->VertexBuffer || Link->VertexBuffer->getReferenceCount() == 1) {
deleteHardwareBuffer(Link); for (size_t i = 0; i < HWBufferList.size(); ) {
} else { auto *Link = HWBufferList[i];
if (!Link->IndexBuffer || Link->IndexBuffer->getReferenceCount() == 1)
deleteHardwareBuffer(Link); bool del;
} if (Link->IsVertex)
del = !Link->VertexBuffer || Link->VertexBuffer->getReferenceCount() == 1;
else
del = !Link->IndexBuffer || Link->IndexBuffer->getReferenceCount() == 1;
// deleting can reorder, so don't advance in list
if (del)
deleteHardwareBuffer(Link);
else
i++;
} }
FrameStats.HWBuffersActive = HWBufferList.size();
} }
void CNullDriver::deleteHardwareBuffer(SHWBufferLink *HWBuffer) void CNullDriver::deleteHardwareBuffer(SHWBufferLink *HWBuffer)
{ {
if (!HWBuffer) if (!HWBuffer)
return; return;
HWBufferList.erase(HWBuffer->listPosition); const size_t pos = HWBuffer->ListPosition;
_IRR_DEBUG_BREAK_IF(HWBufferList.at(pos) != HWBuffer)
if (HWBufferList.size() < 2 || pos == HWBufferList.size() - 1) {
HWBufferList.erase(HWBufferList.begin() + pos);
} else {
// swap with last
std::swap(HWBufferList[pos], HWBufferList.back());
HWBufferList.pop_back();
HWBufferList[pos]->ListPosition = pos;
}
delete HWBuffer; delete HWBuffer;
} }
void CNullDriver::updateHardwareBuffer(const scene::IVertexBuffer *vb)
{
if (!vb)
return;
auto *link = getBufferLink(vb);
if (link)
updateHardwareBuffer(link);
}
void CNullDriver::updateHardwareBuffer(const scene::IIndexBuffer *ib)
{
if (!ib)
return;
auto *link = getBufferLink(ib);
if (link)
updateHardwareBuffer(link);
}
void CNullDriver::removeHardwareBuffer(const scene::IVertexBuffer *vb) void CNullDriver::removeHardwareBuffer(const scene::IVertexBuffer *vb)
{ {
if (!vb) if (!vb)
@ -1484,34 +1520,24 @@ IGPUProgrammingServices *CNullDriver::getGPUProgrammingServices()
//! Adds a new material renderer to the VideoDriver, based on a high level shading language. //! Adds a new material renderer to the VideoDriver, based on a high level shading language.
s32 CNullDriver::addHighLevelShaderMaterial( s32 CNullDriver::addHighLevelShaderMaterial(
const c8 *vertexShaderProgram, const c8 *vertexShaderProgram,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8 *pixelShaderProgram, const c8 *pixelShaderProgram,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const c8 *geometryShaderProgram, const c8 *geometryShaderProgram,
const c8 *geometryShaderEntryPointName, const c8 *shaderName,
E_GEOMETRY_SHADER_TYPE gsCompileTarget,
scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType, scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType,
u32 verticesOut, u32 verticesOut,
IShaderConstantSetCallBack *callback, IShaderConstantSetCallBack *callback,
E_MATERIAL_TYPE baseMaterial, E_MATERIAL_TYPE baseMaterial,
s32 userData) s32 userData)
{ {
os::Printer::log("High level shader materials not available (yet) in this driver, sorry"); os::Printer::log("Shader materials not available in this driver", ELL_ERROR);
return -1; return -1;
} }
s32 CNullDriver::addHighLevelShaderMaterialFromFiles( s32 CNullDriver::addHighLevelShaderMaterialFromFiles(
const io::path &vertexShaderProgramFileName, const io::path &vertexShaderProgramFileName,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const io::path &pixelShaderProgramFileName, const io::path &pixelShaderProgramFileName,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const io::path &geometryShaderProgramFileName, const io::path &geometryShaderProgramFileName,
const c8 *geometryShaderEntryPointName, const c8 *shaderName,
E_GEOMETRY_SHADER_TYPE gsCompileTarget,
scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType, scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType,
u32 verticesOut, u32 verticesOut,
IShaderConstantSetCallBack *callback, IShaderConstantSetCallBack *callback,
@ -1547,9 +1573,7 @@ s32 CNullDriver::addHighLevelShaderMaterialFromFiles(
} }
s32 result = addHighLevelShaderMaterialFromFiles( s32 result = addHighLevelShaderMaterialFromFiles(
vsfile, vertexShaderEntryPointName, vsCompileTarget, vsfile, psfile, gsfile, shaderName,
psfile, pixelShaderEntryPointName, psCompileTarget,
gsfile, geometryShaderEntryPointName, gsCompileTarget,
inType, outType, verticesOut, inType, outType, verticesOut,
callback, baseMaterial, userData); callback, baseMaterial, userData);
@ -1567,14 +1591,9 @@ s32 CNullDriver::addHighLevelShaderMaterialFromFiles(
s32 CNullDriver::addHighLevelShaderMaterialFromFiles( s32 CNullDriver::addHighLevelShaderMaterialFromFiles(
io::IReadFile *vertexShaderProgram, io::IReadFile *vertexShaderProgram,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
io::IReadFile *pixelShaderProgram, io::IReadFile *pixelShaderProgram,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
io::IReadFile *geometryShaderProgram, io::IReadFile *geometryShaderProgram,
const c8 *geometryShaderEntryPointName, const c8 *shaderName,
E_GEOMETRY_SHADER_TYPE gsCompileTarget,
scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType, scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType,
u32 verticesOut, u32 verticesOut,
IShaderConstantSetCallBack *callback, IShaderConstantSetCallBack *callback,
@ -1620,9 +1639,7 @@ s32 CNullDriver::addHighLevelShaderMaterialFromFiles(
} }
s32 result = this->addHighLevelShaderMaterial( s32 result = this->addHighLevelShaderMaterial(
vs, vertexShaderEntryPointName, vsCompileTarget, vs, ps, gs, shaderName,
ps, pixelShaderEntryPointName, psCompileTarget,
gs, geometryShaderEntryPointName, gsCompileTarget,
inType, outType, verticesOut, inType, outType, verticesOut,
callback, baseMaterial, userData); callback, baseMaterial, userData);

View file

@ -17,7 +17,6 @@
#include "S3DVertex.h" #include "S3DVertex.h"
#include "SVertexIndex.h" #include "SVertexIndex.h"
#include "SExposedVideoData.h" #include "SExposedVideoData.h"
#include <list>
namespace irr namespace irr
{ {
@ -293,7 +292,7 @@ protected:
struct SHWBufferLink struct SHWBufferLink
{ {
SHWBufferLink(const scene::IVertexBuffer *vb) : SHWBufferLink(const scene::IVertexBuffer *vb) :
VertexBuffer(vb), ChangedID(0), IsVertex(true) VertexBuffer(vb), IsVertex(true)
{ {
if (VertexBuffer) { if (VertexBuffer) {
VertexBuffer->grab(); VertexBuffer->grab();
@ -301,7 +300,7 @@ protected:
} }
} }
SHWBufferLink(const scene::IIndexBuffer *ib) : SHWBufferLink(const scene::IIndexBuffer *ib) :
IndexBuffer(ib), ChangedID(0), IsVertex(false) IndexBuffer(ib), IsVertex(false)
{ {
if (IndexBuffer) { if (IndexBuffer) {
IndexBuffer->grab(); IndexBuffer->grab();
@ -324,9 +323,9 @@ protected:
const scene::IVertexBuffer *VertexBuffer; const scene::IVertexBuffer *VertexBuffer;
const scene::IIndexBuffer *IndexBuffer; const scene::IIndexBuffer *IndexBuffer;
}; };
u32 ChangedID; size_t ListPosition = static_cast<size_t>(-1);
u32 ChangedID = 0;
bool IsVertex; bool IsVertex;
std::list<SHWBufferLink*>::iterator listPosition;
}; };
//! Gets hardware buffer link from a vertex buffer (may create or update buffer) //! Gets hardware buffer link from a vertex buffer (may create or update buffer)
@ -348,6 +347,10 @@ protected:
virtual SHWBufferLink *createHardwareBuffer(const scene::IIndexBuffer *ib) { return 0; } virtual SHWBufferLink *createHardwareBuffer(const scene::IIndexBuffer *ib) { return 0; }
public: public:
virtual void updateHardwareBuffer(const scene::IVertexBuffer *vb) override;
virtual void updateHardwareBuffer(const scene::IIndexBuffer *ib) override;
//! Remove hardware buffer //! Remove hardware buffer
void removeHardwareBuffer(const scene::IVertexBuffer *vb) override; void removeHardwareBuffer(const scene::IVertexBuffer *vb) override;
@ -357,8 +360,8 @@ public:
//! Remove all hardware buffers //! Remove all hardware buffers
void removeAllHardwareBuffers() override; void removeAllHardwareBuffers() override;
//! Update all hardware buffers, remove unused ones //! Run garbage-collection on all HW buffers
virtual void updateAllHardwareBuffers(); void expireHardwareBuffers();
//! is vbo recommended? //! is vbo recommended?
virtual bool isHardwareBufferRecommend(const scene::IVertexBuffer *mb); virtual bool isHardwareBufferRecommend(const scene::IVertexBuffer *mb);
@ -446,54 +449,39 @@ public:
//! Adds a new material renderer to the VideoDriver, based on a high level shading language. //! Adds a new material renderer to the VideoDriver, based on a high level shading language.
virtual s32 addHighLevelShaderMaterial( virtual s32 addHighLevelShaderMaterial(
const c8 *vertexShaderProgram, const c8 *vertexShaderProgram,
const c8 *vertexShaderEntryPointName = 0, const c8 *pixelShaderProgram,
E_VERTEX_SHADER_TYPE vsCompileTarget = EVST_VS_1_1, const c8 *geometryShaderProgram,
const c8 *pixelShaderProgram = 0, const c8 *shaderName = nullptr,
const c8 *pixelShaderEntryPointName = 0,
E_PIXEL_SHADER_TYPE psCompileTarget = EPST_PS_1_1,
const c8 *geometryShaderProgram = 0,
const c8 *geometryShaderEntryPointName = "main",
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES, scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0, u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0, IShaderConstantSetCallBack *callback = nullptr,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID, E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0) override; s32 userData = 0)override;
virtual s32 addHighLevelShaderMaterialFromFiles( virtual s32 addHighLevelShaderMaterialFromFiles(
const io::path &vertexShaderProgramFile, const io::path &vertexShaderProgramFileName,
const c8 *vertexShaderEntryPointName = "main", const io::path &pixelShaderProgramFileName,
E_VERTEX_SHADER_TYPE vsCompileTarget = EVST_VS_1_1, const io::path &geometryShaderProgramFileName,
const io::path &pixelShaderProgramFile = "", const c8 *shaderName = nullptr,
const c8 *pixelShaderEntryPointName = "main",
E_PIXEL_SHADER_TYPE psCompileTarget = EPST_PS_1_1,
const io::path &geometryShaderProgramFileName = "",
const c8 *geometryShaderEntryPointName = "main",
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES, scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0, u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0, IShaderConstantSetCallBack *callback = nullptr,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID, E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0) override; s32 userData = 0) override;
virtual s32 addHighLevelShaderMaterialFromFiles( s32 addHighLevelShaderMaterialFromFiles(
io::IReadFile *vertexShaderProgram, io::IReadFile *vertexShaderProgram,
const c8 *vertexShaderEntryPointName = "main",
E_VERTEX_SHADER_TYPE vsCompileTarget = EVST_VS_1_1,
io::IReadFile *pixelShaderProgram = 0, io::IReadFile *pixelShaderProgram = 0,
const c8 *pixelShaderEntryPointName = "main",
E_PIXEL_SHADER_TYPE psCompileTarget = EPST_PS_1_1,
io::IReadFile *geometryShaderProgram = 0, io::IReadFile *geometryShaderProgram = 0,
const c8 *geometryShaderEntryPointName = "main", const c8 *shaderName = nullptr,
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES, scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0, u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0, IShaderConstantSetCallBack *callback = nullptr,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID, E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0) override; s32 userData = 0);
virtual void deleteShaderMaterial(s32 material) override; virtual void deleteShaderMaterial(s32 material) override;
@ -578,13 +566,16 @@ protected:
//! deletes all material renderers //! deletes all material renderers
void deleteMaterialRenders(); void deleteMaterialRenders();
// adds a created hardware buffer to the relevant data structure
void registerHardwareBuffer(SHWBufferLink *HWBuffer);
// prints renderer version // prints renderer version
void printVersion(); void printVersion();
inline void accountHWBufferUpload(u32 size) inline void accountHWBufferUpload(u32 size)
{ {
FrameStats.HWBuffersUploaded++; FrameStats.HWBuffersUploaded++;
FrameStats.HWBuffersUploadedSize += size; (void)size;
} }
inline bool getWriteZBuffer(const SMaterial &material) const inline bool getWriteZBuffer(const SMaterial &material) const
@ -701,7 +692,7 @@ protected:
core::array<video::IImageWriter *> SurfaceWriter; core::array<video::IImageWriter *> SurfaceWriter;
core::array<SMaterialRenderer> MaterialRenderers; core::array<SMaterialRenderer> MaterialRenderers;
std::list<SHWBufferLink *> HWBufferList; std::vector<SHWBufferLink *> HWBufferList;
io::IFileSystem *FileSystem; io::IFileSystem *FileSystem;

View file

@ -430,9 +430,7 @@ COpenGLDriver::SHWBufferLink *COpenGLDriver::createHardwareBuffer(const scene::I
return 0; return 0;
SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(vb); SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(vb);
registerHardwareBuffer(HWBuffer);
// add to map
HWBuffer->listPosition = HWBufferList.insert(HWBufferList.end(), HWBuffer);
if (!updateVertexHardwareBuffer(HWBuffer)) { if (!updateVertexHardwareBuffer(HWBuffer)) {
deleteHardwareBuffer(HWBuffer); deleteHardwareBuffer(HWBuffer);
@ -453,9 +451,7 @@ COpenGLDriver::SHWBufferLink *COpenGLDriver::createHardwareBuffer(const scene::I
return 0; return 0;
SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(ib); SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(ib);
registerHardwareBuffer(HWBuffer);
// add to map
HWBuffer->listPosition = HWBufferList.insert(HWBufferList.end(), HWBuffer);
if (!updateIndexHardwareBuffer(HWBuffer)) { if (!updateIndexHardwareBuffer(HWBuffer)) {
deleteHardwareBuffer(HWBuffer); deleteHardwareBuffer(HWBuffer);
@ -2658,14 +2654,9 @@ bool COpenGLDriver::setPixelShaderConstant(s32 index, const u32 *ints, int count
//! Adds a new material renderer to the VideoDriver, using GLSL to render geometry. //! Adds a new material renderer to the VideoDriver, using GLSL to render geometry.
s32 COpenGLDriver::addHighLevelShaderMaterial( s32 COpenGLDriver::addHighLevelShaderMaterial(
const c8 *vertexShaderProgram, const c8 *vertexShaderProgram,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8 *pixelShaderProgram, const c8 *pixelShaderProgram,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const c8 *geometryShaderProgram, const c8 *geometryShaderProgram,
const c8 *geometryShaderEntryPointName, const c8 *shaderName,
E_GEOMETRY_SHADER_TYPE gsCompileTarget,
scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE inType,
scene::E_PRIMITIVE_TYPE outType, scene::E_PRIMITIVE_TYPE outType,
u32 verticesOut, u32 verticesOut,
@ -2677,9 +2668,9 @@ s32 COpenGLDriver::addHighLevelShaderMaterial(
COpenGLSLMaterialRenderer *r = new COpenGLSLMaterialRenderer( COpenGLSLMaterialRenderer *r = new COpenGLSLMaterialRenderer(
this, nr, this, nr,
vertexShaderProgram, vertexShaderEntryPointName, vsCompileTarget, vertexShaderProgram,
pixelShaderProgram, pixelShaderEntryPointName, psCompileTarget, pixelShaderProgram,
geometryShaderProgram, geometryShaderEntryPointName, gsCompileTarget, geometryShaderProgram,
inType, outType, verticesOut, inType, outType, verticesOut,
callback, baseMaterial, userData); callback, baseMaterial, userData);

View file

@ -240,18 +240,13 @@ public:
//! Adds a new material renderer to the VideoDriver, using GLSL to render geometry. //! Adds a new material renderer to the VideoDriver, using GLSL to render geometry.
virtual s32 addHighLevelShaderMaterial( virtual s32 addHighLevelShaderMaterial(
const c8 *vertexShaderProgram, const c8 *vertexShaderProgram,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8 *pixelShaderProgram, const c8 *pixelShaderProgram,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const c8 *geometryShaderProgram, const c8 *geometryShaderProgram,
const c8 *geometryShaderEntryPointName = "main", const c8 *shaderName = nullptr,
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES, scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0, u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0, IShaderConstantSetCallBack *callback = nullptr,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID, E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0) override; s32 userData = 0) override;

View file

@ -34,14 +34,8 @@ namespace video
//! Constructor //! Constructor
COpenGLSLMaterialRenderer::COpenGLSLMaterialRenderer(video::COpenGLDriver *driver, COpenGLSLMaterialRenderer::COpenGLSLMaterialRenderer(video::COpenGLDriver *driver,
s32 &outMaterialTypeNr, const c8 *vertexShaderProgram, s32 &outMaterialTypeNr, const c8 *vertexShaderProgram,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8 *pixelShaderProgram, const c8 *pixelShaderProgram,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const c8 *geometryShaderProgram, const c8 *geometryShaderProgram,
const c8 *geometryShaderEntryPointName,
E_GEOMETRY_SHADER_TYPE gsCompileTarget,
scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType, scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType,
u32 verticesOut, u32 verticesOut,
IShaderConstantSetCallBack *callback, IShaderConstantSetCallBack *callback,

View file

@ -33,14 +33,8 @@ public:
COpenGLDriver *driver, COpenGLDriver *driver,
s32 &outMaterialTypeNr, s32 &outMaterialTypeNr,
const c8 *vertexShaderProgram = 0, const c8 *vertexShaderProgram = 0,
const c8 *vertexShaderEntryPointName = 0,
E_VERTEX_SHADER_TYPE vsCompileTarget = video::EVST_VS_1_1,
const c8 *pixelShaderProgram = 0, const c8 *pixelShaderProgram = 0,
const c8 *pixelShaderEntryPointName = 0,
E_PIXEL_SHADER_TYPE psCompileTarget = video::EPST_PS_1_1,
const c8 *geometryShaderProgram = 0, const c8 *geometryShaderProgram = 0,
const c8 *geometryShaderEntryPointName = "main",
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES, scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0, u32 verticesOut = 0,

View file

@ -307,7 +307,7 @@ const core::aabbox3d<f32> &CSceneManager::getBoundingBox() const
{ {
_IRR_DEBUG_BREAK_IF(true) // Bounding Box of Scene Manager should never be used. _IRR_DEBUG_BREAK_IF(true) // Bounding Box of Scene Manager should never be used.
static const core::aabbox3d<f32> dummy; static const core::aabbox3d<f32> dummy{{0.0f, 0.0f, 0.0f}};
return dummy; return dummy;
} }

View file

@ -40,6 +40,8 @@ typedef COpenGLCoreTexture<COpenGL3DriverBase> COpenGL3Texture;
typedef COpenGLCoreRenderTarget<COpenGL3DriverBase, COpenGL3Texture> COpenGL3RenderTarget; typedef COpenGLCoreRenderTarget<COpenGL3DriverBase, COpenGL3Texture> COpenGL3RenderTarget;
typedef COpenGLCoreCacheHandler<COpenGL3DriverBase, COpenGL3Texture> COpenGL3CacheHandler; typedef COpenGLCoreCacheHandler<COpenGL3DriverBase, COpenGL3Texture> COpenGL3CacheHandler;
class OpenGLVBO;
enum OpenGLSpec : u8 enum OpenGLSpec : u8
{ {
Core, Core,

View file

@ -177,6 +177,8 @@ COpenGL3DriverBase::COpenGL3DriverBase(const SIrrlichtCreationParameters &params
COpenGL3DriverBase::~COpenGL3DriverBase() COpenGL3DriverBase::~COpenGL3DriverBase()
{ {
QuadIndexVBO.destroy();
deleteMaterialRenders(); deleteMaterialRenders();
CacheHandler->getTextureCache().clear(); CacheHandler->getTextureCache().clear();
@ -198,12 +200,16 @@ COpenGL3DriverBase::~COpenGL3DriverBase()
} }
} }
void COpenGL3DriverBase::initQuadsIndices(int max_vertex_count) void COpenGL3DriverBase::initQuadsIndices(u32 max_vertex_count)
{ {
int max_quad_count = max_vertex_count / 4; u32 max_quad_count = max_vertex_count / 4;
std::vector<GLushort> QuadsIndices; u32 indices_size = 6 * max_quad_count;
QuadsIndices.reserve(6 * max_quad_count); if (indices_size == QuadIndexVBO.getSize() * sizeof(u16))
for (int k = 0; k < max_quad_count; k++) { return;
// initialize buffer contents
std::vector<u16> QuadsIndices;
QuadsIndices.reserve(indices_size);
for (u32 k = 0; k < max_quad_count; k++) {
QuadsIndices.push_back(4 * k + 0); QuadsIndices.push_back(4 * k + 0);
QuadsIndices.push_back(4 * k + 1); QuadsIndices.push_back(4 * k + 1);
QuadsIndices.push_back(4 * k + 2); QuadsIndices.push_back(4 * k + 2);
@ -211,11 +217,8 @@ void COpenGL3DriverBase::initQuadsIndices(int max_vertex_count)
QuadsIndices.push_back(4 * k + 2); QuadsIndices.push_back(4 * k + 2);
QuadsIndices.push_back(4 * k + 3); QuadsIndices.push_back(4 * k + 3);
} }
GL.GenBuffers(1, &QuadIndexBuffer); QuadIndexVBO.upload(QuadsIndices.data(), QuadsIndices.size() * sizeof(u16),
GL.BindBuffer(GL_ARRAY_BUFFER, QuadIndexBuffer); 0, GL_STATIC_DRAW, true);
GL.BufferData(GL_ARRAY_BUFFER, sizeof(QuadsIndices[0]) * QuadsIndices.size(), QuadsIndices.data(), GL_STATIC_DRAW);
GL.BindBuffer(GL_ARRAY_BUFFER, 0);
QuadIndexCount = QuadsIndices.size();
} }
void COpenGL3DriverBase::initVersion() void COpenGL3DriverBase::initVersion()
@ -383,28 +386,28 @@ void COpenGL3DriverBase::createMaterialRenderers()
// EMT_SOLID // EMT_SOLID
core::stringc FragmentShader = OGLES2ShaderPath + "Solid.fsh"; core::stringc FragmentShader = OGLES2ShaderPath + "Solid.fsh";
addHighLevelShaderMaterialFromFiles(VertexShader, "main", EVST_VS_2_0, FragmentShader, "main", EPST_PS_2_0, "", "main", addHighLevelShaderMaterialFromFiles(VertexShader, FragmentShader, "", "Solid",
EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, SolidCB, EMT_SOLID, 0); scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, SolidCB, EMT_SOLID, 0);
// EMT_TRANSPARENT_ALPHA_CHANNEL // EMT_TRANSPARENT_ALPHA_CHANNEL
FragmentShader = OGLES2ShaderPath + "TransparentAlphaChannel.fsh"; FragmentShader = OGLES2ShaderPath + "TransparentAlphaChannel.fsh";
addHighLevelShaderMaterialFromFiles(VertexShader, "main", EVST_VS_2_0, FragmentShader, "main", EPST_PS_2_0, "", "main", addHighLevelShaderMaterialFromFiles(VertexShader, FragmentShader, "", "TransparentAlphaChannel",
EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, TransparentAlphaChannelCB, EMT_TRANSPARENT_ALPHA_CHANNEL, 0); scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, TransparentAlphaChannelCB, EMT_TRANSPARENT_ALPHA_CHANNEL, 0);
// EMT_TRANSPARENT_ALPHA_CHANNEL_REF // EMT_TRANSPARENT_ALPHA_CHANNEL_REF
FragmentShader = OGLES2ShaderPath + "TransparentAlphaChannelRef.fsh"; FragmentShader = OGLES2ShaderPath + "TransparentAlphaChannelRef.fsh";
addHighLevelShaderMaterialFromFiles(VertexShader, "main", EVST_VS_2_0, FragmentShader, "main", EPST_PS_2_0, "", "main", addHighLevelShaderMaterialFromFiles(VertexShader, FragmentShader, "", "TransparentAlphaChannelRef",
EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, TransparentAlphaChannelRefCB, EMT_SOLID, 0); scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, TransparentAlphaChannelRefCB, EMT_SOLID, 0);
// EMT_TRANSPARENT_VERTEX_ALPHA // EMT_TRANSPARENT_VERTEX_ALPHA
FragmentShader = OGLES2ShaderPath + "TransparentVertexAlpha.fsh"; FragmentShader = OGLES2ShaderPath + "TransparentVertexAlpha.fsh";
addHighLevelShaderMaterialFromFiles(VertexShader, "main", EVST_VS_2_0, FragmentShader, "main", EPST_PS_2_0, "", "main", addHighLevelShaderMaterialFromFiles(VertexShader, FragmentShader, "", "TransparentVertexAlpha",
EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, TransparentVertexAlphaCB, EMT_TRANSPARENT_ALPHA_CHANNEL, 0); scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, TransparentVertexAlphaCB, EMT_TRANSPARENT_ALPHA_CHANNEL, 0);
// EMT_ONETEXTURE_BLEND // EMT_ONETEXTURE_BLEND
FragmentShader = OGLES2ShaderPath + "OneTextureBlend.fsh"; FragmentShader = OGLES2ShaderPath + "OneTextureBlend.fsh";
addHighLevelShaderMaterialFromFiles(VertexShader, "main", EVST_VS_2_0, FragmentShader, "main", EPST_PS_2_0, "", "main", addHighLevelShaderMaterialFromFiles(VertexShader, FragmentShader, "", "OneTextureBlend",
EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, OneTextureBlendCB, EMT_ONETEXTURE_BLEND, 0); scene::EPT_TRIANGLES, scene::EPT_TRIANGLE_STRIP, 0, OneTextureBlendCB, EMT_ONETEXTURE_BLEND, 0);
// Drop callbacks. // Drop callbacks.
@ -474,41 +477,18 @@ void COpenGL3DriverBase::setTransform(E_TRANSFORMATION_STATE state, const core::
Transformation3DChanged = true; Transformation3DChanged = true;
} }
bool COpenGL3DriverBase::updateHardwareBuffer(SHWBufferLink_opengl *HWBuffer, bool COpenGL3DriverBase::uploadHardwareBuffer(OpenGLVBO &vbo,
const void *buffer, size_t bufferSize, scene::E_HARDWARE_MAPPING hint) const void *buffer, size_t bufferSize, scene::E_HARDWARE_MAPPING hint)
{ {
assert(HWBuffer);
accountHWBufferUpload(bufferSize); accountHWBufferUpload(bufferSize);
// get or create buffer GLenum usage = GL_STATIC_DRAW;
bool newBuffer = false; if (hint == scene::EHM_STREAM)
if (!HWBuffer->vbo_ID) { usage = GL_STREAM_DRAW;
GL.GenBuffers(1, &HWBuffer->vbo_ID); else if (hint == scene::EHM_DYNAMIC)
if (!HWBuffer->vbo_ID) usage = GL_DYNAMIC_DRAW;
return false;
newBuffer = true;
} else if (HWBuffer->vbo_Size < bufferSize) {
newBuffer = true;
}
GL.BindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_ID); vbo.upload(buffer, bufferSize, 0, usage);
// copy data to graphics card
if (!newBuffer)
GL.BufferSubData(GL_ARRAY_BUFFER, 0, bufferSize, buffer);
else {
HWBuffer->vbo_Size = bufferSize;
GLenum usage = GL_STATIC_DRAW;
if (hint == scene::EHM_STREAM)
usage = GL_STREAM_DRAW;
else if (hint == scene::EHM_DYNAMIC)
usage = GL_DYNAMIC_DRAW;
GL.BufferData(GL_ARRAY_BUFFER, bufferSize, buffer, usage);
}
GL.BindBuffer(GL_ARRAY_BUFFER, 0);
return (!TEST_GL_ERROR(this)); return (!TEST_GL_ERROR(this));
} }
@ -525,7 +505,8 @@ bool COpenGL3DriverBase::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuff
const u32 vertexSize = getVertexPitchFromType(vb->getType()); const u32 vertexSize = getVertexPitchFromType(vb->getType());
const size_t bufferSize = vertexSize * vb->getCount(); const size_t bufferSize = vertexSize * vb->getCount();
return updateHardwareBuffer(HWBuffer, vb->getData(), bufferSize, vb->getHardwareMappingHint()); return uploadHardwareBuffer(HWBuffer->Vbo, vb->getData(),
bufferSize, vb->getHardwareMappingHint());
} }
bool COpenGL3DriverBase::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) bool COpenGL3DriverBase::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer)
@ -551,7 +532,8 @@ bool COpenGL3DriverBase::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffe
const size_t bufferSize = ib->getCount() * indexSize; const size_t bufferSize = ib->getCount() * indexSize;
return updateHardwareBuffer(HWBuffer, ib->getData(), bufferSize, ib->getHardwareMappingHint()); return uploadHardwareBuffer(HWBuffer->Vbo, ib->getData(),
bufferSize, ib->getHardwareMappingHint());
} }
bool COpenGL3DriverBase::updateHardwareBuffer(SHWBufferLink *HWBuffer) bool COpenGL3DriverBase::updateHardwareBuffer(SHWBufferLink *HWBuffer)
@ -563,14 +545,14 @@ bool COpenGL3DriverBase::updateHardwareBuffer(SHWBufferLink *HWBuffer)
if (b->IsVertex) { if (b->IsVertex) {
assert(b->VertexBuffer); assert(b->VertexBuffer);
if (b->ChangedID != b->VertexBuffer->getChangedID() || !b->vbo_ID) { if (b->ChangedID != b->VertexBuffer->getChangedID() || !b->Vbo.exists()) {
if (!updateVertexHardwareBuffer(b)) if (!updateVertexHardwareBuffer(b))
return false; return false;
b->ChangedID = b->VertexBuffer->getChangedID(); b->ChangedID = b->VertexBuffer->getChangedID();
} }
} else { } else {
assert(b->IndexBuffer); assert(b->IndexBuffer);
if (b->ChangedID != b->IndexBuffer->getChangedID() || !b->vbo_ID) { if (b->ChangedID != b->IndexBuffer->getChangedID() || !b->Vbo.exists()) {
if (!updateIndexHardwareBuffer(b)) if (!updateIndexHardwareBuffer(b))
return false; return false;
b->ChangedID = b->IndexBuffer->getChangedID(); b->ChangedID = b->IndexBuffer->getChangedID();
@ -584,10 +566,8 @@ COpenGL3DriverBase::SHWBufferLink *COpenGL3DriverBase::createHardwareBuffer(cons
if (!vb || vb->getHardwareMappingHint() == scene::EHM_NEVER) if (!vb || vb->getHardwareMappingHint() == scene::EHM_NEVER)
return 0; return 0;
SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(vb); auto *HWBuffer = new SHWBufferLink_opengl(vb);
registerHardwareBuffer(HWBuffer);
// add to map
HWBuffer->listPosition = HWBufferList.insert(HWBufferList.end(), HWBuffer);
if (!updateVertexHardwareBuffer(HWBuffer)) { if (!updateVertexHardwareBuffer(HWBuffer)) {
deleteHardwareBuffer(HWBuffer); deleteHardwareBuffer(HWBuffer);
@ -602,10 +582,8 @@ COpenGL3DriverBase::SHWBufferLink *COpenGL3DriverBase::createHardwareBuffer(cons
if (!ib || ib->getHardwareMappingHint() == scene::EHM_NEVER) if (!ib || ib->getHardwareMappingHint() == scene::EHM_NEVER)
return 0; return 0;
SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(ib); auto *HWBuffer = new SHWBufferLink_opengl(ib);
registerHardwareBuffer(HWBuffer);
// add to map
HWBuffer->listPosition = HWBufferList.insert(HWBufferList.end(), HWBuffer);
if (!updateIndexHardwareBuffer(HWBuffer)) { if (!updateIndexHardwareBuffer(HWBuffer)) {
deleteHardwareBuffer(HWBuffer); deleteHardwareBuffer(HWBuffer);
@ -621,10 +599,7 @@ void COpenGL3DriverBase::deleteHardwareBuffer(SHWBufferLink *HWBuffer)
return; return;
auto *b = static_cast<SHWBufferLink_opengl *>(HWBuffer); auto *b = static_cast<SHWBufferLink_opengl *>(HWBuffer);
if (b->vbo_ID) { b->Vbo.destroy();
GL.DeleteBuffers(1, &b->vbo_ID);
b->vbo_ID = 0;
}
CNullDriver::deleteHardwareBuffer(HWBuffer); CNullDriver::deleteHardwareBuffer(HWBuffer);
} }
@ -644,14 +619,14 @@ void COpenGL3DriverBase::drawBuffers(const scene::IVertexBuffer *vb,
const void *vertices = vb->getData(); const void *vertices = vb->getData();
if (hwvert) { if (hwvert) {
assert(hwvert->IsVertex); assert(hwvert->IsVertex);
GL.BindBuffer(GL_ARRAY_BUFFER, hwvert->vbo_ID); GL.BindBuffer(GL_ARRAY_BUFFER, hwvert->Vbo.getName());
vertices = nullptr; vertices = nullptr;
} }
const void *indexList = ib->getData(); const void *indexList = ib->getData();
if (hwidx) { if (hwidx) {
assert(!hwidx->IsVertex); assert(!hwidx->IsVertex);
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, hwidx->vbo_ID); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, hwidx->Vbo.getName());
indexList = nullptr; indexList = nullptr;
} }
@ -710,58 +685,32 @@ void COpenGL3DriverBase::drawVertexPrimitiveList(const void *vertices, u32 verte
setRenderStates3DMode(); setRenderStates3DMode();
auto &vTypeDesc = getVertexTypeDescription(vType); drawGeneric(vertices, indexList, primitiveCount, vType, pType, iType);
beginDraw(vTypeDesc, reinterpret_cast<uintptr_t>(vertices));
GLenum indexSize = 0;
switch (iType) {
case (EIT_16BIT): {
indexSize = GL_UNSIGNED_SHORT;
break;
}
case (EIT_32BIT): {
indexSize = GL_UNSIGNED_INT;
break;
}
}
switch (pType) {
case scene::EPT_POINTS:
case scene::EPT_POINT_SPRITES:
GL.DrawArrays(GL_POINTS, 0, primitiveCount);
break;
case scene::EPT_LINE_STRIP:
GL.DrawElements(GL_LINE_STRIP, primitiveCount + 1, indexSize, indexList);
break;
case scene::EPT_LINE_LOOP:
GL.DrawElements(GL_LINE_LOOP, primitiveCount, indexSize, indexList);
break;
case scene::EPT_LINES:
GL.DrawElements(GL_LINES, primitiveCount * 2, indexSize, indexList);
break;
case scene::EPT_TRIANGLE_STRIP:
GL.DrawElements(GL_TRIANGLE_STRIP, primitiveCount + 2, indexSize, indexList);
break;
case scene::EPT_TRIANGLE_FAN:
GL.DrawElements(GL_TRIANGLE_FAN, primitiveCount + 2, indexSize, indexList);
break;
case scene::EPT_TRIANGLES:
GL.DrawElements((LastMaterial.Wireframe) ? GL_LINES : (LastMaterial.PointCloud) ? GL_POINTS
: GL_TRIANGLES,
primitiveCount * 3, indexSize, indexList);
break;
default:
break;
}
endDraw(vTypeDesc);
} }
//! draws a vertex primitive list in 2d
void COpenGL3DriverBase::draw2DVertexPrimitiveList(const void *vertices, u32 vertexCount, void COpenGL3DriverBase::draw2DVertexPrimitiveList(const void *vertices, u32 vertexCount,
const void *indexList, u32 primitiveCount, const void *indexList, u32 primitiveCount,
E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType) E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType)
{ {
os::Printer::log("draw2DVertexPrimitiveList unimplemented", ELL_ERROR); if (!primitiveCount || !vertexCount)
return;
if (!vertices)
return;
if (!checkPrimitiveCount(primitiveCount))
return;
CNullDriver::draw2DVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType, iType);
setRenderStates2DMode(
Material.MaterialType == EMT_TRANSPARENT_VERTEX_ALPHA,
Material.getTexture(0),
Material.MaterialType == EMT_TRANSPARENT_ALPHA_CHANNEL
);
drawGeneric(vertices, indexList, primitiveCount, vType, pType, iType);
} }
void COpenGL3DriverBase::draw2DImage(const video::ITexture *texture, const core::position2d<s32> &destPos, void COpenGL3DriverBase::draw2DImage(const video::ITexture *texture, const core::position2d<s32> &destPos,
@ -824,10 +773,10 @@ void COpenGL3DriverBase::draw2DImage(const video::ITexture *texture, const core:
clipRect->getWidth(), clipRect->getHeight()); clipRect->getWidth(), clipRect->getHeight());
} }
f32 left = (f32)destRect.UpperLeftCorner.X / (f32)renderTargetSize.Width * 2.f - 1.f; f32 left = (f32)destRect.UpperLeftCorner.X;
f32 right = (f32)destRect.LowerRightCorner.X / (f32)renderTargetSize.Width * 2.f - 1.f; f32 right = (f32)destRect.LowerRightCorner.X;
f32 down = 2.f - (f32)destRect.LowerRightCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f; f32 down = (f32)destRect.LowerRightCorner.Y;
f32 top = 2.f - (f32)destRect.UpperLeftCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f; f32 top = (f32)destRect.UpperLeftCorner.Y;
S3DVertex vertices[4]; S3DVertex vertices[4];
vertices[0] = S3DVertex(left, top, 0, 0, 0, 1, useColor[0], tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y); vertices[0] = S3DVertex(left, top, 0, 0, 0, 1, useColor[0], tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
@ -903,7 +852,7 @@ void COpenGL3DriverBase::draw2DImageBatch(const video::ITexture *texture,
} }
const irr::u32 drawCount = core::min_<u32>(positions.size(), sourceRects.size()); const irr::u32 drawCount = core::min_<u32>(positions.size(), sourceRects.size());
assert(6 * drawCount <= QuadIndexCount); // FIXME split the batch? or let it crash? assert(6 * drawCount * sizeof(u16) <= QuadIndexVBO.getSize()); // FIXME split the batch? or let it crash?
std::vector<S3DVertex> vtx; std::vector<S3DVertex> vtx;
vtx.reserve(drawCount * 4); vtx.reserve(drawCount * 4);
@ -924,10 +873,10 @@ void COpenGL3DriverBase::draw2DImageBatch(const video::ITexture *texture,
const core::rect<s32> poss(targetPos, sourceSize); const core::rect<s32> poss(targetPos, sourceSize);
f32 left = (f32)poss.UpperLeftCorner.X / (f32)renderTargetSize.Width * 2.f - 1.f; f32 left = (f32)poss.UpperLeftCorner.X;
f32 right = (f32)poss.LowerRightCorner.X / (f32)renderTargetSize.Width * 2.f - 1.f; f32 right = (f32)poss.LowerRightCorner.X;
f32 down = 2.f - (f32)poss.LowerRightCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f; f32 down = (f32)poss.LowerRightCorner.Y;
f32 top = 2.f - (f32)poss.UpperLeftCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f; f32 top = (f32)poss.UpperLeftCorner.Y;
vtx.emplace_back(left, top, 0.0f, vtx.emplace_back(left, top, 0.0f,
0.0f, 0.0f, 0.0f, color, 0.0f, 0.0f, 0.0f, color,
@ -943,7 +892,7 @@ void COpenGL3DriverBase::draw2DImageBatch(const video::ITexture *texture,
tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y); tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
} }
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, QuadIndexBuffer); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, QuadIndexVBO.getName());
drawElements(GL_TRIANGLES, vt2DImage, vtx.data(), vtx.size(), 0, 6 * drawCount); drawElements(GL_TRIANGLES, vt2DImage, vtx.data(), vtx.size(), 0, 6 * drawCount);
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
@ -956,33 +905,7 @@ void COpenGL3DriverBase::draw2DRectangle(SColor color,
const core::rect<s32> &position, const core::rect<s32> &position,
const core::rect<s32> *clip) const core::rect<s32> *clip)
{ {
chooseMaterial2D(); draw2DRectangle(position, color, color, color, color, clip);
setMaterialTexture(0, 0);
setRenderStates2DMode(color.getAlpha() < 255, false, false);
core::rect<s32> pos = position;
if (clip)
pos.clipAgainst(*clip);
if (!pos.isValid())
return;
const core::dimension2d<u32> &renderTargetSize = getCurrentRenderTargetSize();
f32 left = (f32)pos.UpperLeftCorner.X / (f32)renderTargetSize.Width * 2.f - 1.f;
f32 right = (f32)pos.LowerRightCorner.X / (f32)renderTargetSize.Width * 2.f - 1.f;
f32 down = 2.f - (f32)pos.LowerRightCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f;
f32 top = 2.f - (f32)pos.UpperLeftCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f;
S3DVertex vertices[4];
vertices[0] = S3DVertex(left, top, 0, 0, 0, 1, color, 0, 0);
vertices[1] = S3DVertex(right, top, 0, 0, 0, 1, color, 0, 0);
vertices[2] = S3DVertex(right, down, 0, 0, 0, 1, color, 0, 0);
vertices[3] = S3DVertex(left, down, 0, 0, 0, 1, color, 0, 0);
drawQuad(vtPrimitive, vertices);
} }
//! draw an 2d rectangle //! draw an 2d rectangle
@ -1008,18 +931,16 @@ void COpenGL3DriverBase::draw2DRectangle(const core::rect<s32> &position,
colorRightDown.getAlpha() < 255, colorRightDown.getAlpha() < 255,
false, false); false, false);
const core::dimension2d<u32> &renderTargetSize = getCurrentRenderTargetSize(); f32 left = (f32)pos.UpperLeftCorner.X;
f32 right = (f32)pos.LowerRightCorner.X;
f32 left = (f32)pos.UpperLeftCorner.X / (f32)renderTargetSize.Width * 2.f - 1.f; f32 down = (f32)pos.LowerRightCorner.Y;
f32 right = (f32)pos.LowerRightCorner.X / (f32)renderTargetSize.Width * 2.f - 1.f; f32 top = (f32)pos.UpperLeftCorner.Y;
f32 down = 2.f - (f32)pos.LowerRightCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f;
f32 top = 2.f - (f32)pos.UpperLeftCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f;
S3DVertex vertices[4]; S3DVertex vertices[4];
vertices[0] = S3DVertex(left, top, 0, 0, 0, 1, colorLeftUp, 0, 0); vertices[0] = S3DVertex(left, top, 0, 0, 0, 1, colorLeftUp, 0, 0);
vertices[1] = S3DVertex(right, top, 0, 0, 0, 1, colorRightUp, 0, 0); vertices[1] = S3DVertex(right, top, 0, 0, 0, 1, colorRightUp, 0, 0);
vertices[2] = S3DVertex(right, down, 0, 0, 0, 1, colorRightDown, 0, 0); vertices[2] = S3DVertex(right, down, 0, 0, 0, 1, colorRightDown, 0, 0);
vertices[3] = S3DVertex(left, down, 0, 0, 0, 1, colorLeftDown, 0, 0); vertices[3] = S3DVertex(left, down, 0, 0, 0, 1, colorLeftDown, 0, 0);
drawQuad(vtPrimitive, vertices); drawQuad(vtPrimitive, vertices);
} }
@ -1034,12 +955,10 @@ void COpenGL3DriverBase::draw2DLine(const core::position2d<s32> &start,
setRenderStates2DMode(color.getAlpha() < 255, false, false); setRenderStates2DMode(color.getAlpha() < 255, false, false);
const core::dimension2d<u32> &renderTargetSize = getCurrentRenderTargetSize(); f32 startX = (f32)start.X;
f32 endX = (f32)end.X;
f32 startX = (f32)start.X / (f32)renderTargetSize.Width * 2.f - 1.f; f32 startY = (f32)start.Y;
f32 endX = (f32)end.X / (f32)renderTargetSize.Width * 2.f - 1.f; f32 endY = (f32)end.Y;
f32 startY = 2.f - (f32)start.Y / (f32)renderTargetSize.Height * 2.f - 1.f;
f32 endY = 2.f - (f32)end.Y / (f32)renderTargetSize.Height * 2.f - 1.f;
S3DVertex vertices[2]; S3DVertex vertices[2];
vertices[0] = S3DVertex(startX, startY, 0, 0, 0, 1, color, 0, 0); vertices[0] = S3DVertex(startX, startY, 0, 0, 0, 1, color, 0, 0);
@ -1068,6 +987,55 @@ void COpenGL3DriverBase::drawElements(GLenum primitiveType, const VertexType &ve
endDraw(vertexType); endDraw(vertexType);
} }
void COpenGL3DriverBase::drawGeneric(const void *vertices, const void *indexList,
u32 primitiveCount,
E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType)
{
auto &vTypeDesc = getVertexTypeDescription(vType);
beginDraw(vTypeDesc, reinterpret_cast<uintptr_t>(vertices));
GLenum indexSize = 0;
switch (iType) {
case EIT_16BIT:
indexSize = GL_UNSIGNED_SHORT;
break;
case EIT_32BIT:
indexSize = GL_UNSIGNED_INT;
break;
}
switch (pType) {
case scene::EPT_POINTS:
case scene::EPT_POINT_SPRITES:
GL.DrawArrays(GL_POINTS, 0, primitiveCount);
break;
case scene::EPT_LINE_STRIP:
GL.DrawElements(GL_LINE_STRIP, primitiveCount + 1, indexSize, indexList);
break;
case scene::EPT_LINE_LOOP:
GL.DrawElements(GL_LINE_LOOP, primitiveCount, indexSize, indexList);
break;
case scene::EPT_LINES:
GL.DrawElements(GL_LINES, primitiveCount * 2, indexSize, indexList);
break;
case scene::EPT_TRIANGLE_STRIP:
GL.DrawElements(GL_TRIANGLE_STRIP, primitiveCount + 2, indexSize, indexList);
break;
case scene::EPT_TRIANGLE_FAN:
GL.DrawElements(GL_TRIANGLE_FAN, primitiveCount + 2, indexSize, indexList);
break;
case scene::EPT_TRIANGLES:
GL.DrawElements((LastMaterial.Wireframe) ? GL_LINES : (LastMaterial.PointCloud) ? GL_POINTS
: GL_TRIANGLES,
primitiveCount * 3, indexSize, indexList);
break;
default:
break;
}
endDraw(vTypeDesc);
}
void COpenGL3DriverBase::beginDraw(const VertexType &vertexType, uintptr_t verticesBase) void COpenGL3DriverBase::beginDraw(const VertexType &vertexType, uintptr_t verticesBase)
{ {
for (auto &attr : vertexType) { for (auto &attr : vertexType) {
@ -1372,60 +1340,74 @@ void COpenGL3DriverBase::setTextureRenderStates(const SMaterial &material, bool
CacheHandler->setActiveTexture(GL_TEXTURE0 + i); CacheHandler->setActiveTexture(GL_TEXTURE0 + i);
if (resetAllRenderstates) const auto &layer = material.TextureLayers[i];
tmpTexture->getStatesCache().IsCached = false; auto &states = tmpTexture->getStatesCache();
if (!tmpTexture->getStatesCache().IsCached || material.TextureLayers[i].MagFilter != tmpTexture->getStatesCache().MagFilter) { if (resetAllRenderstates)
E_TEXTURE_MAG_FILTER magFilter = material.TextureLayers[i].MagFilter; states.IsCached = false;
if (!states.IsCached || layer.MagFilter != states.MagFilter) {
E_TEXTURE_MAG_FILTER magFilter = layer.MagFilter;
GL.TexParameteri(tmpTextureType, GL_TEXTURE_MAG_FILTER, GL.TexParameteri(tmpTextureType, GL_TEXTURE_MAG_FILTER,
magFilter == ETMAGF_NEAREST ? GL_NEAREST : (assert(magFilter == ETMAGF_LINEAR), GL_LINEAR)); magFilter == ETMAGF_NEAREST ? GL_NEAREST : (assert(magFilter == ETMAGF_LINEAR), GL_LINEAR));
tmpTexture->getStatesCache().MagFilter = magFilter; states.MagFilter = magFilter;
} }
if (material.UseMipMaps && tmpTexture->hasMipMaps()) { if (material.UseMipMaps && tmpTexture->hasMipMaps()) {
if (!tmpTexture->getStatesCache().IsCached || material.TextureLayers[i].MinFilter != tmpTexture->getStatesCache().MinFilter || if (!states.IsCached || layer.MinFilter != states.MinFilter ||
!tmpTexture->getStatesCache().MipMapStatus) { !states.MipMapStatus) {
E_TEXTURE_MIN_FILTER minFilter = material.TextureLayers[i].MinFilter; E_TEXTURE_MIN_FILTER minFilter = layer.MinFilter;
GL.TexParameteri(tmpTextureType, GL_TEXTURE_MIN_FILTER, GL.TexParameteri(tmpTextureType, GL_TEXTURE_MIN_FILTER,
minFilter == ETMINF_NEAREST_MIPMAP_NEAREST ? GL_NEAREST_MIPMAP_NEAREST : minFilter == ETMINF_LINEAR_MIPMAP_NEAREST ? GL_LINEAR_MIPMAP_NEAREST minFilter == ETMINF_NEAREST_MIPMAP_NEAREST ? GL_NEAREST_MIPMAP_NEAREST : minFilter == ETMINF_LINEAR_MIPMAP_NEAREST ? GL_LINEAR_MIPMAP_NEAREST
: minFilter == ETMINF_NEAREST_MIPMAP_LINEAR ? GL_NEAREST_MIPMAP_LINEAR : minFilter == ETMINF_NEAREST_MIPMAP_LINEAR ? GL_NEAREST_MIPMAP_LINEAR
: (assert(minFilter == ETMINF_LINEAR_MIPMAP_LINEAR), GL_LINEAR_MIPMAP_LINEAR)); : (assert(minFilter == ETMINF_LINEAR_MIPMAP_LINEAR), GL_LINEAR_MIPMAP_LINEAR));
tmpTexture->getStatesCache().MinFilter = minFilter; states.MinFilter = minFilter;
tmpTexture->getStatesCache().MipMapStatus = true; states.MipMapStatus = true;
} }
} else { } else {
if (!tmpTexture->getStatesCache().IsCached || material.TextureLayers[i].MinFilter != tmpTexture->getStatesCache().MinFilter || if (!states.IsCached || layer.MinFilter != states.MinFilter ||
tmpTexture->getStatesCache().MipMapStatus) { states.MipMapStatus) {
E_TEXTURE_MIN_FILTER minFilter = material.TextureLayers[i].MinFilter; E_TEXTURE_MIN_FILTER minFilter = layer.MinFilter;
GL.TexParameteri(tmpTextureType, GL_TEXTURE_MIN_FILTER, GL.TexParameteri(tmpTextureType, GL_TEXTURE_MIN_FILTER,
(minFilter == ETMINF_NEAREST_MIPMAP_NEAREST || minFilter == ETMINF_NEAREST_MIPMAP_LINEAR) ? GL_NEAREST : (assert(minFilter == ETMINF_LINEAR_MIPMAP_NEAREST || minFilter == ETMINF_LINEAR_MIPMAP_LINEAR), GL_LINEAR)); (minFilter == ETMINF_NEAREST_MIPMAP_NEAREST || minFilter == ETMINF_NEAREST_MIPMAP_LINEAR) ? GL_NEAREST : (assert(minFilter == ETMINF_LINEAR_MIPMAP_NEAREST || minFilter == ETMINF_LINEAR_MIPMAP_LINEAR), GL_LINEAR));
tmpTexture->getStatesCache().MinFilter = minFilter; states.MinFilter = minFilter;
tmpTexture->getStatesCache().MipMapStatus = false; states.MipMapStatus = false;
} }
} }
if (LODBiasSupported &&
(!states.IsCached || layer.LODBias != states.LODBias)) {
if (layer.LODBias) {
const float tmp = core::clamp(layer.LODBias * 0.125f, -MaxTextureLODBias, MaxTextureLODBias);
GL.TexParameterf(tmpTextureType, GL.TEXTURE_LOD_BIAS, tmp);
} else
GL.TexParameterf(tmpTextureType, GL.TEXTURE_LOD_BIAS, 0.f);
states.LODBias = layer.LODBias;
}
if (AnisotropicFilterSupported && if (AnisotropicFilterSupported &&
(!tmpTexture->getStatesCache().IsCached || material.TextureLayers[i].AnisotropicFilter != tmpTexture->getStatesCache().AnisotropicFilter)) { (!states.IsCached || layer.AnisotropicFilter != states.AnisotropicFilter)) {
GL.TexParameteri(tmpTextureType, GL.TEXTURE_MAX_ANISOTROPY, GL.TexParameteri(tmpTextureType, GL.TEXTURE_MAX_ANISOTROPY,
material.TextureLayers[i].AnisotropicFilter > 1 ? core::min_(MaxAnisotropy, material.TextureLayers[i].AnisotropicFilter) : 1); layer.AnisotropicFilter > 1 ? core::min_(MaxAnisotropy, layer.AnisotropicFilter) : 1);
tmpTexture->getStatesCache().AnisotropicFilter = material.TextureLayers[i].AnisotropicFilter; states.AnisotropicFilter = layer.AnisotropicFilter;
} }
if (!tmpTexture->getStatesCache().IsCached || material.TextureLayers[i].TextureWrapU != tmpTexture->getStatesCache().WrapU) { if (!states.IsCached || layer.TextureWrapU != states.WrapU) {
GL.TexParameteri(tmpTextureType, GL_TEXTURE_WRAP_S, getTextureWrapMode(material.TextureLayers[i].TextureWrapU)); GL.TexParameteri(tmpTextureType, GL_TEXTURE_WRAP_S, getTextureWrapMode(layer.TextureWrapU));
tmpTexture->getStatesCache().WrapU = material.TextureLayers[i].TextureWrapU; states.WrapU = layer.TextureWrapU;
} }
if (!tmpTexture->getStatesCache().IsCached || material.TextureLayers[i].TextureWrapV != tmpTexture->getStatesCache().WrapV) { if (!states.IsCached || layer.TextureWrapV != states.WrapV) {
GL.TexParameteri(tmpTextureType, GL_TEXTURE_WRAP_T, getTextureWrapMode(material.TextureLayers[i].TextureWrapV)); GL.TexParameteri(tmpTextureType, GL_TEXTURE_WRAP_T, getTextureWrapMode(layer.TextureWrapV));
tmpTexture->getStatesCache().WrapV = material.TextureLayers[i].TextureWrapV; states.WrapV = layer.TextureWrapV;
} }
tmpTexture->getStatesCache().IsCached = true; states.IsCached = true;
} }
} }
@ -1620,14 +1602,9 @@ bool COpenGL3DriverBase::setPixelShaderConstant(s32 index, const u32 *ints, int
//! Adds a new material renderer to the VideoDriver, using GLSL to render geometry. //! Adds a new material renderer to the VideoDriver, using GLSL to render geometry.
s32 COpenGL3DriverBase::addHighLevelShaderMaterial( s32 COpenGL3DriverBase::addHighLevelShaderMaterial(
const c8 *vertexShaderProgram, const c8 *vertexShaderProgram,
const c8 *vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8 *pixelShaderProgram, const c8 *pixelShaderProgram,
const c8 *pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const c8 *geometryShaderProgram, const c8 *geometryShaderProgram,
const c8 *geometryShaderEntryPointName, const c8 *shaderName,
E_GEOMETRY_SHADER_TYPE gsCompileTarget,
scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE inType,
scene::E_PRIMITIVE_TYPE outType, scene::E_PRIMITIVE_TYPE outType,
u32 verticesOut, u32 verticesOut,

View file

@ -8,6 +8,7 @@
#include "SIrrCreationParameters.h" #include "SIrrCreationParameters.h"
#include "Common.h" #include "Common.h"
#include "VBO.h"
#include "CNullDriver.h" #include "CNullDriver.h"
#include "IMaterialRendererServices.h" #include "IMaterialRendererServices.h"
#include "EDriverFeatures.h" #include "EDriverFeatures.h"
@ -49,8 +50,7 @@ public:
SHWBufferLink_opengl(const scene::IVertexBuffer *vb) : SHWBufferLink(vb) {} SHWBufferLink_opengl(const scene::IVertexBuffer *vb) : SHWBufferLink(vb) {}
SHWBufferLink_opengl(const scene::IIndexBuffer *ib) : SHWBufferLink(ib) {} SHWBufferLink_opengl(const scene::IIndexBuffer *ib) : SHWBufferLink(ib) {}
GLuint vbo_ID = 0; OpenGLVBO Vbo;
u32 vbo_Size = 0;
}; };
bool updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer); bool updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer);
@ -189,18 +189,13 @@ public:
//! Adds a new material renderer to the VideoDriver //! Adds a new material renderer to the VideoDriver
virtual s32 addHighLevelShaderMaterial( virtual s32 addHighLevelShaderMaterial(
const c8 *vertexShaderProgram, const c8 *vertexShaderProgram,
const c8 *vertexShaderEntryPointName = 0, const c8 *pixelShaderProgram,
E_VERTEX_SHADER_TYPE vsCompileTarget = EVST_VS_1_1, const c8 *geometryShaderProgram = nullptr,
const c8 *pixelShaderProgram = 0, const c8 *shaderName = nullptr,
const c8 *pixelShaderEntryPointName = 0,
E_PIXEL_SHADER_TYPE psCompileTarget = EPST_PS_1_1,
const c8 *geometryShaderProgram = 0,
const c8 *geometryShaderEntryPointName = "main",
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES, scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0, u32 verticesOut = 0,
IShaderConstantSetCallBack *callback = 0, IShaderConstantSetCallBack *callback = nullptr,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID, E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0) override; s32 userData = 0) override;
@ -300,7 +295,7 @@ protected:
LockRenderStateMode = false; LockRenderStateMode = false;
} }
bool updateHardwareBuffer(SHWBufferLink_opengl *b, const void *buffer, size_t bufferSize, scene::E_HARDWARE_MAPPING hint); bool uploadHardwareBuffer(OpenGLVBO &vbo, const void *buffer, size_t bufferSize, scene::E_HARDWARE_MAPPING hint);
void createMaterialRenderers(); void createMaterialRenderers();
@ -316,6 +311,9 @@ protected:
void drawElements(GLenum primitiveType, const VertexType &vertexType, const void *vertices, int vertexCount, const u16 *indices, int indexCount); void drawElements(GLenum primitiveType, const VertexType &vertexType, const void *vertices, int vertexCount, const u16 *indices, int indexCount);
void drawElements(GLenum primitiveType, const VertexType &vertexType, uintptr_t vertices, uintptr_t indices, int indexCount); void drawElements(GLenum primitiveType, const VertexType &vertexType, uintptr_t vertices, uintptr_t indices, int indexCount);
void drawGeneric(const void *vertices, const void *indexList, u32 primitiveCount,
E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType);
void beginDraw(const VertexType &vertexType, uintptr_t verticesBase); void beginDraw(const VertexType &vertexType, uintptr_t verticesBase);
void endDraw(const VertexType &vertexType); void endDraw(const VertexType &vertexType);
@ -372,9 +370,8 @@ private:
bool EnableErrorTest; bool EnableErrorTest;
unsigned QuadIndexCount; OpenGLVBO QuadIndexVBO;
GLuint QuadIndexBuffer = 0; void initQuadsIndices(u32 max_vertex_count = 65536);
void initQuadsIndices(int max_vertex_count = 65536);
void debugCb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message); void debugCb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message);
static void APIENTRY debugCb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); static void APIENTRY debugCb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam);

View file

@ -161,6 +161,7 @@ public:
GL.BlendEquation(mode); GL.BlendEquation(mode);
} }
bool LODBiasSupported = false;
bool AnisotropicFilterSupported = false; bool AnisotropicFilterSupported = false;
bool BlendMinMaxSupported = false; bool BlendMinMaxSupported = false;
bool TextureMultisampleSupported = false; bool TextureMultisampleSupported = false;

View file

@ -32,6 +32,7 @@ COpenGL3Renderer2D::COpenGL3Renderer2D(const c8 *vertexShaderProgram, const c8 *
// These states don't change later. // These states don't change later.
ProjectionID = getPixelShaderConstantID("uProjection");
ThicknessID = getPixelShaderConstantID("uThickness"); ThicknessID = getPixelShaderConstantID("uThickness");
if (WithTexture) { if (WithTexture) {
TextureUsageID = getPixelShaderConstantID("uTextureUsage"); TextureUsageID = getPixelShaderConstantID("uTextureUsage");
@ -62,6 +63,17 @@ void COpenGL3Renderer2D::OnSetMaterial(const video::SMaterial &material,
f32 Thickness = (material.Thickness > 0.f) ? material.Thickness : 1.f; f32 Thickness = (material.Thickness > 0.f) ? material.Thickness : 1.f;
setPixelShaderConstant(ThicknessID, &Thickness, 1); setPixelShaderConstant(ThicknessID, &Thickness, 1);
{
// Update projection matrix
const core::dimension2d<u32> renderTargetSize = Driver->getCurrentRenderTargetSize();
core::matrix4 proj;
float xInv2 = 2.0f / renderTargetSize.Width;
float yInv2 = 2.0f / renderTargetSize.Height;
proj.setScale({ xInv2, -yInv2, 0.0f });
proj.setTranslation({ -1.0f, 1.0f, 0.0f });
setPixelShaderConstant(ProjectionID, proj.pointer(), 4 * 4);
}
if (WithTexture) { if (WithTexture) {
s32 TextureUsage = material.TextureLayers[0].Texture ? 1 : 0; s32 TextureUsage = material.TextureLayers[0].Texture ? 1 : 0;
setPixelShaderConstant(TextureUsageID, &TextureUsage, 1); setPixelShaderConstant(TextureUsageID, &TextureUsage, 1);

View file

@ -24,6 +24,7 @@ public:
protected: protected:
bool WithTexture; bool WithTexture;
s32 ProjectionID;
s32 ThicknessID; s32 ThicknessID;
s32 TextureUsageID; s32 TextureUsageID;
}; };

51
irr/src/OpenGL/VBO.cpp Normal file
View file

@ -0,0 +1,51 @@
// Copyright (C) 2024 sfan5
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "VBO.h"
#include <cassert>
#include <mt_opengl.h>
namespace irr
{
namespace video
{
void OpenGLVBO::upload(const void *data, size_t size, size_t offset,
GLenum usage, bool mustShrink)
{
bool newBuffer = false;
assert(!(mustShrink && offset > 0)); // forbidden usage
if (!m_name) {
GL.GenBuffers(1, &m_name);
if (!m_name)
return;
newBuffer = true;
} else if (size > m_size || mustShrink) {
newBuffer = size != m_size;
}
GL.BindBuffer(GL_ARRAY_BUFFER, m_name);
if (newBuffer) {
assert(offset == 0);
GL.BufferData(GL_ARRAY_BUFFER, size, data, usage);
m_size = size;
} else {
GL.BufferSubData(GL_ARRAY_BUFFER, offset, size, data);
}
GL.BindBuffer(GL_ARRAY_BUFFER, 0);
}
void OpenGLVBO::destroy()
{
if (m_name)
GL.DeleteBuffers(1, &m_name);
m_name = 0;
m_size = 0;
}
}
}

57
irr/src/OpenGL/VBO.h Normal file
View file

@ -0,0 +1,57 @@
// Copyright (C) 2024 sfan5
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#pragma once
#include "Common.h"
#include <cstddef>
namespace irr
{
namespace video
{
class OpenGLVBO
{
public:
/// @note does not create on GL side
OpenGLVBO() = default;
/// @note does not free on GL side
~OpenGLVBO() = default;
/// @return "name" (ID) of this buffer in GL
GLuint getName() const { return m_name; }
/// @return does this refer to an existing GL buffer?
bool exists() const { return m_name != 0; }
/// @return size of this buffer in bytes
size_t getSize() const { return m_size; }
/**
* Upload buffer data to GL.
*
* Changing the size of the buffer is only possible when `offset == 0`.
* @param data data pointer
* @param size number of bytes
* @param offset offset to upload at
* @param usage usage pattern passed to GL (only if buffer is new)
* @param mustShrink force re-create of buffer if it became smaller
* @note modifies GL_ARRAY_BUFFER binding
*/
void upload(const void *data, size_t size, size_t offset,
GLenum usage, bool mustShrink = false);
/**
* Free buffer in GL.
* @note modifies GL_ARRAY_BUFFER binding
*/
void destroy();
private:
GLuint m_name = 0;
size_t m_size = 0;
};
}
}

View file

@ -69,6 +69,7 @@ void COpenGL3Driver::initFeatures()
TextureFormats[ECF_D24S8] = {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}; TextureFormats[ECF_D24S8] = {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8};
AnisotropicFilterSupported = isVersionAtLeast(4, 6) || queryExtension("GL_ARB_texture_filter_anisotropic") || queryExtension("GL_EXT_texture_filter_anisotropic"); AnisotropicFilterSupported = isVersionAtLeast(4, 6) || queryExtension("GL_ARB_texture_filter_anisotropic") || queryExtension("GL_EXT_texture_filter_anisotropic");
LODBiasSupported = true;
BlendMinMaxSupported = true; BlendMinMaxSupported = true;
TextureMultisampleSupported = true; TextureMultisampleSupported = true;

View file

@ -13,7 +13,7 @@ namespace video
/// OpenGL 3+ driver /// OpenGL 3+ driver
/// ///
/// For OpenGL 3.2 and higher. Compatibility profile is required currently. /// For OpenGL 3.2 and higher. Compatibility profile is required currently.
class COpenGL3Driver : public COpenGL3DriverBase class COpenGL3Driver final : public COpenGL3DriverBase
{ {
friend IVideoDriver *createOpenGL3Driver(const SIrrlichtCreationParameters &params, io::IFileSystem *io, IContextManager *contextManager); friend IVideoDriver *createOpenGL3Driver(const SIrrlichtCreationParameters &params, io::IFileSystem *io, IContextManager *contextManager);

View file

@ -120,10 +120,10 @@ void COpenGLES2Driver::initFeatures()
} }
const bool MRTSupported = Version.Major >= 3 || queryExtension("GL_EXT_draw_buffers"); const bool MRTSupported = Version.Major >= 3 || queryExtension("GL_EXT_draw_buffers");
LODBiasSupported = queryExtension("GL_EXT_texture_lod_bias");
AnisotropicFilterSupported = queryExtension("GL_EXT_texture_filter_anisotropic"); AnisotropicFilterSupported = queryExtension("GL_EXT_texture_filter_anisotropic");
BlendMinMaxSupported = (Version.Major >= 3) || FeatureAvailable[IRR_GL_EXT_blend_minmax]; BlendMinMaxSupported = (Version.Major >= 3) || FeatureAvailable[IRR_GL_EXT_blend_minmax];
TextureMultisampleSupported = isVersionAtLeast(3, 1); TextureMultisampleSupported = isVersionAtLeast(3, 1);
const bool TextureLODBiasSupported = queryExtension("GL_EXT_texture_lod_bias");
// COGLESCoreExtensionHandler::Feature // COGLESCoreExtensionHandler::Feature
static_assert(MATERIAL_MAX_TEXTURES <= 8, "Only up to 8 textures are guaranteed"); static_assert(MATERIAL_MAX_TEXTURES <= 8, "Only up to 8 textures are guaranteed");
@ -141,7 +141,7 @@ void COpenGLES2Driver::initFeatures()
if (Version.Major >= 3 || queryExtension("GL_EXT_draw_range_elements")) if (Version.Major >= 3 || queryExtension("GL_EXT_draw_range_elements"))
MaxIndices = GetInteger(GL_MAX_ELEMENTS_INDICES); MaxIndices = GetInteger(GL_MAX_ELEMENTS_INDICES);
MaxTextureSize = GetInteger(GL_MAX_TEXTURE_SIZE); MaxTextureSize = GetInteger(GL_MAX_TEXTURE_SIZE);
if (TextureLODBiasSupported) if (LODBiasSupported)
GL.GetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &MaxTextureLODBias); GL.GetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &MaxTextureLODBias);
GL.GetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, DimAliasedLine); // NOTE: this is not in the OpenGL ES 2.0 spec... GL.GetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, DimAliasedLine); // NOTE: this is not in the OpenGL ES 2.0 spec...
GL.GetFloatv(GL_ALIASED_POINT_SIZE_RANGE, DimAliasedPoint); GL.GetFloatv(GL_ALIASED_POINT_SIZE_RANGE, DimAliasedPoint);

View file

@ -13,7 +13,7 @@ namespace video
/// OpenGL ES 2+ driver /// OpenGL ES 2+ driver
/// ///
/// For OpenGL ES 2.0 and higher. /// For OpenGL ES 2.0 and higher.
class COpenGLES2Driver : public COpenGL3DriverBase class COpenGLES2Driver final : public COpenGL3DriverBase
{ {
friend IVideoDriver *createOGLES2Driver(const SIrrlichtCreationParameters &params, io::IFileSystem *io, IContextManager *contextManager); friend IVideoDriver *createOGLES2Driver(const SIrrlichtCreationParameters &params, io::IFileSystem *io, IContextManager *contextManager);

View file

@ -573,7 +573,7 @@ SkinnedMesh *SkinnedMeshBuilder::finalize()
return this; return this;
} }
void SkinnedMesh::updateBoundingBox(void) void SkinnedMesh::updateBoundingBox()
{ {
if (!SkinningBuffers) if (!SkinningBuffers)
return; return;

View file

@ -160,11 +160,12 @@ void Printer::print(const c8 *message, ELOG_LEVEL ll)
// Android logcat restricts log-output and cuts the rest of the message away. But we want it all. // Android logcat restricts log-output and cuts the rest of the message away. But we want it all.
// On my device max-len is 1023 (+ 0 byte). Some websites claim a limit of 4096 so maybe different numbers on different devices. // On my device max-len is 1023 (+ 0 byte). Some websites claim a limit of 4096 so maybe different numbers on different devices.
const size_t maxLogLen = 1023; constexpr size_t maxLogLen = 1023;
size_t msgLen = strlen(message); size_t msgLen = strlen(message);
size_t start = 0; size_t start = 0;
while (msgLen - start > maxLogLen) { while (msgLen - start > maxLogLen) {
__android_log_print(LogLevel, "Irrlicht", "%.*s\n", maxLogLen, &message[start]); __android_log_print(LogLevel, "Irrlicht", "%.*s\n",
static_cast<int>(maxLogLen), &message[start]);
start += maxLogLen; start += maxLogLen;
} }
__android_log_print(LogLevel, "Irrlicht", "%s\n", &message[start]); __android_log_print(LogLevel, "Irrlicht", "%s\n", &message[start]);

View file

@ -101,7 +101,7 @@ std::vector<DistanceSortedActiveObject> ActiveObjectMgr::getActiveSelectableObje
if (!obj) if (!obj)
continue; continue;
aabb3f selection_box; aabb3f selection_box{{0.0f, 0.0f, 0.0f}};
if (!obj->getSelectionBox(&selection_box)) if (!obj->getSelectionBox(&selection_box))
continue; continue;

View file

@ -430,7 +430,7 @@ void ClientEnvironment::getSelectedActiveObjects(
for (const auto &allObject : allObjects) { for (const auto &allObject : allObjects) {
ClientActiveObject *obj = allObject.obj; ClientActiveObject *obj = allObject.obj;
aabb3f selection_box; aabb3f selection_box{{0.0f, 0.0f, 0.0f}};
if (!obj->getSelectionBox(&selection_box)) if (!obj->getSelectionBox(&selection_box))
continue; continue;

View file

@ -301,24 +301,29 @@ void ClientLauncher::init_input()
else else
input = new RealInputHandler(receiver); input = new RealInputHandler(receiver);
if (g_settings->getBool("enable_joysticks")) { if (g_settings->getBool("enable_joysticks"))
irr::core::array<irr::SJoystickInfo> infos; init_joysticks();
std::vector<irr::SJoystickInfo> joystick_infos; }
// Make sure this is called maximum once per void ClientLauncher::init_joysticks()
// irrlicht device, otherwise it will give you {
// multiple events for the same joystick. irr::core::array<irr::SJoystickInfo> infos;
if (m_rendering_engine->get_raw_device()->activateJoysticks(infos)) { std::vector<irr::SJoystickInfo> joystick_infos;
infostream << "Joystick support enabled" << std::endl;
joystick_infos.reserve(infos.size()); // Make sure this is called maximum once per
for (u32 i = 0; i < infos.size(); i++) { // irrlicht device, otherwise it will give you
joystick_infos.push_back(infos[i]); // multiple events for the same joystick.
} if (!m_rendering_engine->get_raw_device()->activateJoysticks(infos)) {
input->joystick.onJoystickConnect(joystick_infos); errorstream << "Could not activate joystick support." << std::endl;
} else { return;
errorstream << "Could not activate joystick support." << std::endl;
}
} }
infostream << "Joystick support enabled" << std::endl;
joystick_infos.reserve(infos.size());
for (u32 i = 0; i < infos.size(); i++) {
joystick_infos.push_back(infos[i]);
}
input->joystick.onJoystickConnect(joystick_infos);
} }
void ClientLauncher::setting_changed_callback(const std::string &name, void *data) void ClientLauncher::setting_changed_callback(const std::string &name, void *data)

View file

@ -26,6 +26,7 @@ private:
void init_args(GameStartData &start_data, const Settings &cmd_args); void init_args(GameStartData &start_data, const Settings &cmd_args);
bool init_engine(); bool init_engine();
void init_input(); void init_input();
void init_joysticks();
static void setting_changed_callback(const std::string &name, void *data); static void setting_changed_callback(const std::string &name, void *data);
void config_guienv(); void config_guienv();

View file

@ -22,16 +22,27 @@
#include <queue> #include <queue>
namespace { namespace {
// A helper struct // data structure that groups block meshes by material
struct MeshBufListMaps struct MeshBufListMaps
{ {
using MeshBufListMap = std::unordered_map< // first = block pos
video::SMaterial, using MeshBuf = std::pair<v3s16, scene::IMeshBuffer*>;
std::vector<std::pair<v3s16, scene::IMeshBuffer *>>
>; using MeshBufList = std::vector<MeshBuf>;
using MeshBufListMap = std::unordered_map<video::SMaterial, MeshBufList>;
std::array<MeshBufListMap, MAX_TILE_LAYERS> maps; std::array<MeshBufListMap, MAX_TILE_LAYERS> maps;
bool empty() const
{
for (auto &map : maps) {
if (!map.empty())
return false;
}
return true;
}
void clear() void clear()
{ {
for (auto &map : maps) for (auto &map : maps)
@ -48,7 +59,67 @@ namespace {
auto &bufs = map[m]; // default constructs if non-existent auto &bufs = map[m]; // default constructs if non-existent
bufs.emplace_back(position, buf); bufs.emplace_back(position, buf);
} }
void addFromBlock(v3s16 block_pos, MapBlockMesh *block_mesh,
video::IVideoDriver *driver);
}; };
// reference to a mesh buffer used when rendering the map.
struct DrawDescriptor {
v3f m_pos; // world translation
bool m_reuse_material:1;
bool m_use_partial_buffer:1;
union {
scene::IMeshBuffer *m_buffer;
const PartialMeshBuffer *m_partial_buffer;
};
DrawDescriptor(v3f pos, scene::IMeshBuffer *buffer, bool reuse_material = true) :
m_pos(pos), m_reuse_material(reuse_material), m_use_partial_buffer(false),
m_buffer(buffer)
{}
DrawDescriptor(v3f pos, const PartialMeshBuffer *buffer) :
m_pos(pos), m_reuse_material(false), m_use_partial_buffer(true),
m_partial_buffer(buffer)
{}
video::SMaterial &getMaterial();
/// @return number of vertices drawn
u32 draw(video::IVideoDriver* driver);
};
using DrawDescriptorList = std::vector<DrawDescriptor>;
/// @brief Append vertices to a mesh buffer
/// @note does not update bounding box!
void appendToMeshBuffer(scene::SMeshBuffer *dst, const scene::IMeshBuffer *src, v3f translate)
{
const size_t vcount = dst->Vertices->Data.size();
const size_t icount = dst->Indices->Data.size();
assert(src->getVertexType() == video::EVT_STANDARD);
const auto vptr = static_cast<const video::S3DVertex*>(src->getVertices());
dst->Vertices->Data.insert(dst->Vertices->Data.end(),
vptr, vptr + src->getVertexCount());
// apply translation
for (size_t j = vcount; j < dst->Vertices->Data.size(); j++)
dst->Vertices->Data[j].Pos += translate;
const auto iptr = src->getIndices();
dst->Indices->Data.insert(dst->Indices->Data.end(),
iptr, iptr + src->getIndexCount());
// fixup indices
if (vcount != 0) {
for (size_t j = icount; j < dst->Indices->Data.size(); j++)
dst->Indices->Data[j] += vcount;
}
}
template <typename T>
inline T subtract_or_zero(T a, T b) {
return b >= a ? T(0) : (a - b);
}
} }
/* /*
@ -90,8 +161,7 @@ ClientMap::ClientMap(
* the class is whith a name ;) Name property cames from ISceneNode base class. * the class is whith a name ;) Name property cames from ISceneNode base class.
*/ */
Name = "ClientMap"; Name = "ClientMap";
m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000, setAutomaticCulling(scene::EAC_OFF);
BS*1000000,BS*1000000,BS*1000000);
for (const auto &name : ClientMap_settings) for (const auto &name : ClientMap_settings)
g_settings->registerChangedCallback(name, on_settings_changed, this); g_settings->registerChangedCallback(name, on_settings_changed, this);
@ -299,7 +369,7 @@ void ClientMap::updateDrawList()
} }
const v3s16 camera_block = getContainerPos(cam_pos_nodes, MAP_BLOCKSIZE); const v3s16 camera_block = getContainerPos(cam_pos_nodes, MAP_BLOCKSIZE);
m_drawlist = std::map<v3s16, MapBlock*, MapBlockComparer>(MapBlockComparer(camera_block)); m_drawlist = decltype(m_drawlist)(MapBlockComparer(camera_block));
auto is_frustum_culled = m_client->getCamera()->getFrustumCuller(); auto is_frustum_culled = m_client->getCamera()->getFrustumCuller();
@ -692,23 +762,132 @@ void ClientMap::touchMapBlocks()
g_profiler->avg("MapBlocks loaded [#]", blocks_loaded); g_profiler->avg("MapBlocks loaded [#]", blocks_loaded);
} }
void MeshBufListMaps::addFromBlock(v3s16 block_pos, MapBlockMesh *block_mesh,
video::IVideoDriver *driver)
{
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
scene::IMesh *mesh = block_mesh->getMesh(layer);
assert(mesh);
u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
auto &material = buf->getMaterial();
auto *rnd = driver->getMaterialRenderer(material.MaterialType);
bool transparent = rnd && rnd->isTransparent();
if (!transparent)
add(buf, block_pos, layer);
}
}
}
/**
* Copy a list of mesh buffers into the draw order, while potentially
* merging some.
* @param src buffer list
* @param dst draw order
* @param get_world_pos returns translation for a buffer
* @param buffer_trash output container for temporary mesh buffers
* @return number of buffers that were merged
*/
template <typename F, typename C>
static u32 transformBuffersToDrawOrder(
const MeshBufListMaps::MeshBufList &src, DrawDescriptorList &draw_order,
F get_world_pos, C &buffer_trash)
{
/**
* This is a tradeoff between time spent merging buffers and time spent
* due to excess drawcalls.
* Testing has shown that the ideal value is in the low hundreds, as extra
* CPU work quickly eats up the benefits.
* In MTG landscape scenes this was found to save around 20-40% of drawcalls.
*
* NOTE: if you attempt to test this with quicktune, it won't give you valid
* results since HW buffers stick around and Irrlicht handles large amounts
* inefficiently.
*
* TODO: as a next step we should cache merged meshes, so they do not need
* to be re-built *and* can be kept in GPU memory.
*/
const u32 target_min_vertices = g_settings->getU32("mesh_buffer_min_vertices");
const auto draw_order_pre = draw_order.size();
auto *driver = RenderingEngine::get_video_driver();
// check if we can even merge anything
u32 can_merge = 0;
u32 total_vtx = 0, total_idx = 0;
for (auto &pair : src) {
if (pair.second->getVertexCount() < target_min_vertices) {
can_merge++;
total_vtx += pair.second->getVertexCount();
total_idx += pair.second->getIndexCount();
}
}
scene::SMeshBuffer *tmp = nullptr;
const auto &finish_buf = [&] () {
if (tmp) {
draw_order.emplace_back(v3f(0), tmp);
total_vtx = subtract_or_zero(total_vtx, tmp->getVertexCount());
total_idx = subtract_or_zero(total_idx, tmp->getIndexCount());
// Upload buffer here explicitly to give the driver some
// extra time to get it ready before drawing.
tmp->setHardwareMappingHint(scene::EHM_STREAM);
driver->updateHardwareBuffer(tmp->getVertexBuffer());
driver->updateHardwareBuffer(tmp->getIndexBuffer());
}
tmp = nullptr;
};
// iterate in reverse to get closest blocks first
for (auto it = src.rbegin(); it != src.rend(); ++it) {
v3f translate = get_world_pos(it->first);
auto *buf = it->second;
if (can_merge < 2 || buf->getVertexCount() >= target_min_vertices) {
draw_order.emplace_back(translate, buf);
continue;
}
bool new_buffer = false;
if (!tmp)
new_buffer = true;
else if (tmp->getVertexCount() + buf->getVertexCount() > U16_MAX)
new_buffer = true;
if (new_buffer) {
finish_buf();
tmp = new scene::SMeshBuffer();
buffer_trash.push_back(tmp);
assert(tmp->getPrimitiveType() == buf->getPrimitiveType());
tmp->Material = buf->getMaterial();
// preallocate
tmp->Vertices->Data.reserve(total_vtx);
tmp->Indices->Data.reserve(total_idx);
}
appendToMeshBuffer(tmp, buf, translate);
}
finish_buf();
// first call needs to set the material
if (draw_order.size() > draw_order_pre)
draw_order[draw_order_pre].m_reuse_material = false;
return can_merge < 2 ? 0 : can_merge;
}
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{ {
ZoneScoped; ZoneScoped;
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; const bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix; std::string prefix;
if (pass == scene::ESNRP_SOLID) if (pass == scene::ESNRP_SOLID)
prefix = "renderMap(SOLID): "; prefix = "renderMap(SOLID): ";
else else
prefix = "renderMap(TRANSPARENT): "; prefix = "renderMap(TRANS): ";
/*
This is called two times per frame, reset on the non-transparent one
*/
if (pass == scene::ESNRP_SOLID)
m_last_drawn_sectors.clear();
/* /*
Get animation parameters Get animation parameters
@ -719,16 +898,16 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
const v3f camera_position = m_camera_position; const v3f camera_position = m_camera_position;
/* const auto mesh_grid = m_client->getMeshGrid();
Get all blocks and draw all visible ones // Gets world position from block map position
*/ const auto get_block_wpos = [&] (v3s16 pos) -> v3f {
return intToFloat(mesh_grid.getMeshPos(pos) * MAP_BLOCKSIZE - m_camera_offset, BS);
};
u32 vertex_count = 0; u32 merged_count = 0;
u32 drawcall_count = 0;
// For limiting number of mesh animations per frame // For limiting number of mesh animations per frame
u32 mesh_animate_count = 0; u32 mesh_animate_count = 0;
//u32 mesh_animate_count_far = 0;
/* /*
Update transparent meshes Update transparent meshes
@ -737,18 +916,18 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
updateTransparentMeshBuffers(); updateTransparentMeshBuffers();
/* /*
Draw the selected MapBlocks Collect everything we need to draw
*/ */
TimeTaker tt_collect("");
MeshBufListMaps grouped_buffers; MeshBufListMaps grouped_buffers;
std::vector<DrawDescriptor> draw_order; std::vector<scene::IMeshBuffer*> buffer_trash;
video::SMaterial previous_material; DrawDescriptorList draw_order;
auto is_frustum_culled = m_client->getCamera()->getFrustumCuller(); auto is_frustum_culled = m_client->getCamera()->getFrustumCuller();
const MeshGrid mesh_grid = m_client->getMeshGrid();
for (auto &i : m_drawlist) { for (auto &i : m_drawlist) {
v3s16 block_pos = i.first; const v3s16 block_pos = i.first;
MapBlock *block = i.second; MapBlock *block = i.second;
MapBlockMesh *block_mesh = block->mesh; MapBlockMesh *block_mesh = block->mesh;
@ -789,49 +968,28 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// In transparent pass, the mesh will give us // In transparent pass, the mesh will give us
// the partial buffers in the correct order // the partial buffers in the correct order
for (auto &buffer : block_mesh->getTransparentBuffers()) for (auto &buffer : block_mesh->getTransparentBuffers())
draw_order.emplace_back(block_pos, &buffer); draw_order.emplace_back(get_block_wpos(block_pos), &buffer);
} } else {
else { // Otherwise, group them
// otherwise, group buffers across meshes grouped_buffers.addFromBlock(block_pos, block_mesh, driver);
// using MeshBufListMaps
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
scene::IMesh *mesh = block_mesh->getMesh(layer);
assert(mesh);
u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
video::SMaterial& material = buf->getMaterial();
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
bool transparent = (rnd && rnd->isTransparent());
if (!transparent) {
if (buf->getVertexCount() == 0)
errorstream << "Block [" << analyze_block(block)
<< "] contains an empty meshbuf" << std::endl;
grouped_buffers.add(buf, block_pos, layer);
}
}
}
} }
} }
// Capture draw order for all solid meshes assert(!is_transparent_pass || grouped_buffers.empty());
for (auto &map : grouped_buffers.maps) { for (auto &map : grouped_buffers.maps) {
for (auto &list : map) { for (auto &list : map) {
// iterate in reverse to draw closest blocks first merged_count += transformBuffersToDrawOrder(
for (auto it = list.second.rbegin(); it != list.second.rend(); ++it) { list.second, draw_order, get_block_wpos, buffer_trash);
draw_order.emplace_back(it->first, it->second, it != list.second.rbegin());
}
} }
} }
TimeTaker draw("Drawing mesh buffers"); g_profiler->avg(prefix + "collecting [ms]", tt_collect.stop(true));
TimeTaker tt_draw("");
core::matrix4 m; // Model matrix core::matrix4 m; // Model matrix
v3f offset = intToFloat(m_camera_offset, BS); u32 vertex_count = 0;
u32 drawcall_count = 0;
u32 material_swaps = 0; u32 material_swaps = 0;
// Render all mesh buffers in order // Render all mesh buffers in order
@ -867,18 +1025,17 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
material.TextureLayers[ShadowRenderer::TEXTURE_LAYER_SHADOW].Texture = nullptr; material.TextureLayers[ShadowRenderer::TEXTURE_LAYER_SHADOW].Texture = nullptr;
} }
v3f block_wpos = intToFloat(mesh_grid.getMeshPos(descriptor.m_pos) * MAP_BLOCKSIZE, BS); m.setTranslation(descriptor.m_pos);
m.setTranslation(block_wpos - offset);
driver->setTransform(video::ETS_WORLD, m); driver->setTransform(video::ETS_WORLD, m);
vertex_count += descriptor.draw(driver); vertex_count += descriptor.draw(driver);
} }
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true)); g_profiler->avg(prefix + "draw meshes [ms]", tt_draw.stop(true));
// Log only on solid pass because values are the same
if (pass == scene::ESNRP_SOLID) { if (pass == scene::ESNRP_SOLID) {
g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count); g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count);
g_profiler->avg(prefix + "merged buffers [#]", merged_count);
} }
if (pass == scene::ESNRP_TRANSPARENT) { if (pass == scene::ESNRP_TRANSPARENT) {
@ -888,6 +1045,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count); g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count); g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
g_profiler->avg(prefix + "material swaps [#]", material_swaps); g_profiler->avg(prefix + "material swaps [#]", material_swaps);
for (auto &x : buffer_trash)
x->drop();
} }
static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step, static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step,
@ -1096,12 +1256,15 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
else else
prefix = "renderMap(SHADOW SOLID): "; prefix = "renderMap(SHADOW SOLID): ";
u32 drawcall_count = 0; const auto mesh_grid = m_client->getMeshGrid();
u32 vertex_count = 0; // Gets world position from block map position
const auto get_block_wpos = [&] (v3s16 pos) -> v3f {
return intToFloat(mesh_grid.getMeshPos(pos) * MAP_BLOCKSIZE - m_camera_offset, BS);
};
MeshBufListMaps grouped_buffers; MeshBufListMaps grouped_buffers;
std::vector<DrawDescriptor> draw_order; std::vector<scene::IMeshBuffer*> buffer_trash;
DrawDescriptorList draw_order;
std::size_t count = 0; std::size_t count = 0;
std::size_t meshes_per_frame = m_drawlist_shadow.size() / total_frames + 1; std::size_t meshes_per_frame = m_drawlist_shadow.size() / total_frames + 1;
@ -1113,7 +1276,6 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
return; return;
} }
const MeshGrid mesh_grid = m_client->getMeshGrid();
for (const auto &i : m_drawlist_shadow) { for (const auto &i : m_drawlist_shadow) {
// only process specific part of the list & break early // only process specific part of the list & break early
++count; ++count;
@ -1136,52 +1298,25 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
// In transparent pass, the mesh will give us // In transparent pass, the mesh will give us
// the partial buffers in the correct order // the partial buffers in the correct order
for (auto &buffer : block->mesh->getTransparentBuffers()) for (auto &buffer : block->mesh->getTransparentBuffers())
draw_order.emplace_back(block_pos, &buffer); draw_order.emplace_back(get_block_wpos(block_pos), &buffer);
} } else {
else { // Otherwise, group them
// otherwise, group buffers across meshes grouped_buffers.addFromBlock(block_pos, block->mesh, driver);
// using MeshBufListMaps
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
assert(mesh);
u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
video::SMaterial &mat = buf->getMaterial();
auto rnd = driver->getMaterialRenderer(mat.MaterialType);
bool transparent = rnd && rnd->isTransparent();
if (!transparent)
grouped_buffers.add(buf, block_pos, layer);
}
}
} }
} }
u32 buffer_count = 0;
for (auto &map : grouped_buffers.maps)
for (auto &list : map)
buffer_count += list.second.size();
draw_order.reserve(draw_order.size() + buffer_count);
// Capture draw order for all solid meshes
for (auto &map : grouped_buffers.maps) { for (auto &map : grouped_buffers.maps) {
for (auto &list : map) { for (auto &list : map) {
// iterate in reverse to draw closest blocks first transformBuffersToDrawOrder(
for (auto it = list.second.rbegin(); it != list.second.rend(); ++it) list.second, draw_order, get_block_wpos, buffer_trash);
draw_order.emplace_back(it->first, it->second, it != list.second.rbegin());
} }
} }
TimeTaker draw("Drawing shadow mesh buffers"); TimeTaker draw("");
core::matrix4 m; // Model matrix core::matrix4 m; // Model matrix
v3f offset = intToFloat(m_camera_offset, BS); u32 drawcall_count = 0;
u32 vertex_count = 0;
u32 material_swaps = 0; u32 material_swaps = 0;
// Render all mesh buffers in order // Render all mesh buffers in order
@ -1221,8 +1356,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
++material_swaps; ++material_swaps;
} }
v3f block_wpos = intToFloat(mesh_grid.getMeshPos(descriptor.m_pos) * MAP_BLOCKSIZE, BS); m.setTranslation(descriptor.m_pos);
m.setTranslation(block_wpos - offset);
driver->setTransform(video::ETS_WORLD, m); driver->setTransform(video::ETS_WORLD, m);
vertex_count += descriptor.draw(driver); vertex_count += descriptor.draw(driver);
@ -1232,12 +1366,16 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
video::SMaterial clean; video::SMaterial clean;
clean.BlendOperation = video::EBO_ADD; clean.BlendOperation = video::EBO_ADD;
driver->setMaterial(clean); // reset material to defaults driver->setMaterial(clean); // reset material to defaults
// FIXME: why is this here?
driver->draw3DLine(v3f(), v3f(), video::SColor(0)); driver->draw3DLine(v3f(), v3f(), video::SColor(0));
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true)); g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count); g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count); g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
g_profiler->avg(prefix + "material swaps [#]", material_swaps); g_profiler->avg(prefix + "material swaps [#]", material_swaps);
for (auto &x : buffer_trash)
x->drop();
} }
/* /*
@ -1347,12 +1485,12 @@ void ClientMap::updateTransparentMeshBuffers()
m_needs_update_transparent_meshes = false; m_needs_update_transparent_meshes = false;
} }
video::SMaterial &ClientMap::DrawDescriptor::getMaterial() video::SMaterial &DrawDescriptor::getMaterial()
{ {
return (m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer)->getMaterial(); return (m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer)->getMaterial();
} }
u32 ClientMap::DrawDescriptor::draw(video::IVideoDriver* driver) u32 DrawDescriptor::draw(video::IVideoDriver* driver)
{ {
if (m_use_partial_buffer) { if (m_use_partial_buffer) {
m_partial_buffer->draw(driver); m_partial_buffer->draw(driver);

View file

@ -115,7 +115,6 @@ private:
// update the vertex order in transparent mesh buffers // update the vertex order in transparent mesh buffers
void updateTransparentMeshBuffers(); void updateTransparentMeshBuffers();
// Orders blocks by distance to the camera // Orders blocks by distance to the camera
class MapBlockComparer class MapBlockComparer
{ {
@ -133,30 +132,6 @@ private:
v3s16 m_camera_block; v3s16 m_camera_block;
}; };
// reference to a mesh buffer used when rendering the map.
struct DrawDescriptor {
v3s16 m_pos;
union {
scene::IMeshBuffer *m_buffer;
const PartialMeshBuffer *m_partial_buffer;
};
bool m_reuse_material:1;
bool m_use_partial_buffer:1;
DrawDescriptor(v3s16 pos, scene::IMeshBuffer *buffer, bool reuse_material) :
m_pos(pos), m_buffer(buffer), m_reuse_material(reuse_material), m_use_partial_buffer(false)
{}
DrawDescriptor(v3s16 pos, const PartialMeshBuffer *buffer) :
m_pos(pos), m_partial_buffer(buffer), m_reuse_material(false), m_use_partial_buffer(true)
{}
video::SMaterial &getMaterial();
/// @return index count
u32 draw(video::IVideoDriver* driver);
};
Client *m_client; Client *m_client;
RenderingEngine *m_rendering_engine; RenderingEngine *m_rendering_engine;
@ -177,8 +152,6 @@ private:
std::map<v3s16, MapBlock*> m_drawlist_shadow; std::map<v3s16, MapBlock*> m_drawlist_shadow;
bool m_needs_update_drawlist; bool m_needs_update_drawlist;
std::set<v2s16> m_last_drawn_sectors;
bool m_cache_trilinear_filter; bool m_cache_trilinear_filter;
bool m_cache_bilinear_filter; bool m_cache_bilinear_filter;
bool m_cache_anistropic_filter; bool m_cache_anistropic_filter;

View file

@ -160,7 +160,7 @@ private:
// Was the mesh ever generated? // Was the mesh ever generated?
bool m_mesh_valid = false; bool m_mesh_valid = false;
aabb3f m_box; aabb3f m_box{{0.0f, 0.0f, 0.0f}};
v2f m_origin; v2f m_origin;
u16 m_cloud_radius_i; u16 m_cloud_radius_i;
u32 m_seed; u32 m_seed;

View file

@ -13,7 +13,6 @@
#include "nodedef.h" #include "nodedef.h"
#include "client/tile.h" #include "client/tile.h"
#include "mesh.h" #include "mesh.h"
#include <IMeshManipulator.h>
#include "client/meshgen/collector.h" #include "client/meshgen/collector.h"
#include "client/renderingengine.h" #include "client/renderingengine.h"
#include "client.h" #include "client.h"
@ -60,12 +59,10 @@ static const auto &quad_indices = quad_indices_02;
const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike"; const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";
MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output, MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output):
scene::IMeshManipulator *mm):
data(input), data(input),
collector(output), collector(output),
nodedef(data->nodedef), nodedef(data->nodedef),
meshmanip(mm),
blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE), blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE),
smooth_liquids(g_settings->getBool("enable_water_reflections")) smooth_liquids(g_settings->getBool("enable_water_reflections"))
{ {

View file

@ -5,7 +5,6 @@
#pragma once #pragma once
#include "nodedef.h" #include "nodedef.h"
#include <IMeshManipulator.h>
struct MeshMakeData; struct MeshMakeData;
struct MeshCollector; struct MeshCollector;
@ -46,8 +45,7 @@ struct LightFrame {
class MapblockMeshGenerator class MapblockMeshGenerator
{ {
public: public:
MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output, MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output);
scene::IMeshManipulator *mm);
void generate(); void generate();
void renderSingle(content_t node, u8 param2 = 0x00); void renderSingle(content_t node, u8 param2 = 0x00);
@ -56,7 +54,6 @@ private:
MeshCollector *const collector; MeshCollector *const collector;
const NodeDefManager *const nodedef; const NodeDefManager *const nodedef;
scene::IMeshManipulator *const meshmanip;
const v3s16 blockpos_nodes; const v3s16 blockpos_nodes;

View file

@ -140,7 +140,7 @@ unsigned int FontEngine::getLineHeight(const FontSpec &spec)
gui::IGUIFont *font = getFont(spec); gui::IGUIFont *font = getFont(spec);
return font->getDimension(L"Some unimportant example String").Height return font->getDimension(L"Some unimportant example String").Height
+ font->getKerningHeight(); + font->getKerning(L'S').Y;
} }
/******************************************************************************/ /******************************************************************************/

View file

@ -1752,8 +1752,8 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
if (stats2.Drawcalls > 0) if (stats2.Drawcalls > 0)
g_profiler->avg("Irr: primitives per drawcall", g_profiler->avg("Irr: primitives per drawcall",
stats2.PrimitivesDrawn / float(stats2.Drawcalls)); stats2.PrimitivesDrawn / float(stats2.Drawcalls));
g_profiler->avg("Irr: buffers uploaded", stats2.HWBuffersUploaded); g_profiler->avg("Irr: HW buffers uploaded", stats2.HWBuffersUploaded);
g_profiler->avg("Irr: buffers uploaded (bytes)", stats2.HWBuffersUploadedSize); g_profiler->avg("Irr: HW buffers active", stats2.HWBuffersActive);
} }
void Game::updateStats(RunStats *stats, const FpsControl &draw_times, void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
@ -3246,7 +3246,7 @@ PointedThing Game::updatePointedThing(
hud->pointing_at_object = true; hud->pointing_at_object = true;
runData.selected_object = client->getEnv().getActiveObject(result.object_id); runData.selected_object = client->getEnv().getActiveObject(result.object_id);
aabb3f selection_box; aabb3f selection_box{{0.0f, 0.0f, 0.0f}};
if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() && if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() &&
runData.selected_object->getSelectionBox(&selection_box)) { runData.selected_object->getSelectionBox(&selection_box)) {
v3f pos = runData.selected_object->getPosition(); v3f pos = runData.selected_object->getPosition();

View file

@ -104,11 +104,11 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
os << std::fixed os << std::fixed
<< PROJECT_NAME_C " " << g_version_hash << PROJECT_NAME_C " " << g_version_hash
<< " | FPS: " << fps << " | FPS: " << fps
<< std::setprecision(0) << std::setprecision(fps >= 100 ? 1 : 0)
<< " | drawtime: " << m_drawtime_avg << "ms" << " | drawtime: " << m_drawtime_avg << "ms"
<< std::setprecision(1) << std::setprecision(1)
<< " | dtime jitter: " << " | dtime jitter: "
<< (stats.dtime_jitter.max_fraction * 100.0) << "%" << (stats.dtime_jitter.max_fraction * 100.0f) << "%"
<< std::setprecision(1) << std::setprecision(1)
<< " | view range: " << " | view range: "
<< (draw_control->range_all ? "All" : itos(draw_control->wanted_range)) << (draw_control->range_all ? "All" : itos(draw_control->wanted_range))

View file

@ -874,7 +874,6 @@ void Hud::drawSelectionMesh()
{ {
if (m_mode == HIGHLIGHT_NONE || (m_mode == HIGHLIGHT_HALO && !m_selection_mesh)) if (m_mode == HIGHLIGHT_NONE || (m_mode == HIGHLIGHT_HALO && !m_selection_mesh))
return; return;
const video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material); driver->setMaterial(m_selection_material);
const core::matrix4 oldtransform = driver->getTransform(video::ETS_WORLD); const core::matrix4 oldtransform = driver->getTransform(video::ETS_WORLD);
@ -910,7 +909,6 @@ void Hud::drawSelectionMesh()
driver->drawMeshBuffer(buf); driver->drawMeshBuffer(buf);
} }
} }
driver->setMaterial(oldmaterial);
driver->setTransform(video::ETS_WORLD, oldtransform); driver->setTransform(video::ETS_WORLD, oldtransform);
} }
@ -935,17 +933,11 @@ void Hud::drawBlockBounds()
return; return;
} }
video::SMaterial old_material = driver->getMaterial2D();
driver->setMaterial(m_block_bounds_material); driver->setMaterial(m_block_bounds_material);
u16 mesh_chunk_size = std::max<u16>(1, g_settings->getU16("client_mesh_chunk")); u16 mesh_chunk_size = std::max<u16>(1, g_settings->getU16("client_mesh_chunk"));
v3s16 pos = player->getStandingNodePos(); v3s16 block_pos = getContainerPos(player->getStandingNodePos(), MAP_BLOCKSIZE);
v3s16 block_pos(
floorf((float) pos.X / MAP_BLOCKSIZE),
floorf((float) pos.Y / MAP_BLOCKSIZE),
floorf((float) pos.Z / MAP_BLOCKSIZE)
);
v3f cam_offset = intToFloat(client->getCamera()->getOffset(), BS); v3f cam_offset = intToFloat(client->getCamera()->getOffset(), BS);
@ -988,8 +980,6 @@ void Hud::drawBlockBounds()
choose_color(block_pos.Y, block_pos.Z) choose_color(block_pos.Y, block_pos.Z)
); );
} }
driver->setMaterial(old_material);
} }
void Hud::updateSelectionMesh(const v3s16 &camera_offset) void Hud::updateSelectionMesh(const v3s16 &camera_offset)

View file

@ -148,17 +148,6 @@ static void imageCleanTransparentWithInlining(video::IImage *src, u32 threshold)
} }
} }
/* Fill in RGB values for transparent pixels, to correct for odd colors
* appearing at borders when blending. This is because many PNG optimizers
* like to discard RGB values of transparent pixels, but when blending then
* with non-transparent neighbors, their RGB values will show up nonetheless.
*
* This function modifies the original image in-place.
*
* Parameter "threshold" is the alpha level below which pixels are considered
* transparent. Should be 127 when the texture is used with ALPHA_CHANNEL_REF,
* 0 when alpha blending is used.
*/
void imageCleanTransparent(video::IImage *src, u32 threshold) void imageCleanTransparent(video::IImage *src, u32 threshold)
{ {
if (src->getColorFormat() == video::ECF_A8R8G8B8) if (src->getColorFormat() == video::ECF_A8R8G8B8)
@ -167,13 +156,109 @@ void imageCleanTransparent(video::IImage *src, u32 threshold)
imageCleanTransparentWithInlining<false>(src, threshold); imageCleanTransparentWithInlining<false>(src, threshold);
} }
/* Scale a region of an image into another image, using nearest-neighbor with /**********************************/
* anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
* to prevent non-integer scaling ratio artifacts. Note that this may cause namespace {
* some blending at the edges where pixels don't line up perfectly, but this // For more colorspace transformations, see for example
* filter is designed to produce the most accurate results for both upscaling // <https://github.com/tobspr/GLSL-Color-Spaces/blob/master/ColorSpaces.inc.glsl>
* and downscaling.
*/ inline float linear_to_srgb_component(float v)
{
if (v > 0.0031308f)
return 1.055f * powf(v, 1.0f / 2.4f) - 0.055f;
return 12.92f * v;
}
inline float srgb_to_linear_component(float v)
{
if (v > 0.04045f)
return powf((v + 0.055f) / 1.055f, 2.4f);
return v / 12.92f;
}
template <float (*F)(float)>
struct LUT8 {
std::array<float, 256> t;
LUT8() {
for (size_t i = 0; i < t.size(); i++)
t[i] = F(i / 255.0f);
}
};
LUT8<srgb_to_linear_component> srgb_to_linear_lut;
v3f srgb_to_linear(const video::SColor col_srgb)
{
v3f col(srgb_to_linear_lut.t[col_srgb.getRed()],
srgb_to_linear_lut.t[col_srgb.getGreen()],
srgb_to_linear_lut.t[col_srgb.getBlue()]);
return col;
}
video::SColor linear_to_srgb(const v3f col_linear)
{
v3f col;
// we can't LUT this without losing precision, but thankfully we call
// it just once :)
col.X = linear_to_srgb_component(col_linear.X);
col.Y = linear_to_srgb_component(col_linear.Y);
col.Z = linear_to_srgb_component(col_linear.Z);
col *= 255.0f;
col.X = core::clamp<float>(col.X, 0.0f, 255.0f);
col.Y = core::clamp<float>(col.Y, 0.0f, 255.0f);
col.Z = core::clamp<float>(col.Z, 0.0f, 255.0f);
return video::SColor(0xff, myround(col.X), myround(col.Y),
myround(col.Z));
}
}
template <bool IS_A8R8G8B8>
static video::SColor imageAverageColorInline(const video::IImage *src)
{
void *const src_data = src->getData();
const core::dimension2du dim = src->getDimension();
auto get_pixel = [=](u32 x, u32 y) -> video::SColor {
if constexpr (IS_A8R8G8B8) {
return reinterpret_cast<u32 *>(src_data)[y*dim.Width + x];
} else {
return src->getPixel(x, y);
}
};
u32 total = 0;
v3f col_acc;
// limit runtime cost
const u32 stepx = std::max(1U, dim.Width / 16),
stepy = std::max(1U, dim.Height / 16);
for (u32 x = 0; x < dim.Width; x += stepx) {
for (u32 y = 0; y < dim.Height; y += stepy) {
video::SColor c = get_pixel(x, y);
if (c.getAlpha() > 0) {
total++;
col_acc += srgb_to_linear(c);
}
}
}
video::SColor ret(0, 0, 0, 0);
if (total > 0) {
col_acc /= total;
ret = linear_to_srgb(col_acc);
}
ret.setAlpha(255);
return ret;
}
video::SColor imageAverageColor(const video::IImage *img)
{
if (img->getColorFormat() == video::ECF_A8R8G8B8)
return imageAverageColorInline<true>(img);
else
return imageAverageColorInline<false>(img);
}
/**********************************/
void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::IImage *dest) void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::IImage *dest)
{ {
double sx, sy, minsx, maxsx, minsy, maxsy, area, ra, ga, ba, aa, pw, ph, pa; double sx, sy, minsx, maxsx, minsy, maxsy, area, ra, ga, ba, aa, pw, ph, pa;

View file

@ -6,6 +6,7 @@
#include "irrlichttypes.h" #include "irrlichttypes.h"
#include <rect.h> #include <rect.h>
#include <SColor.h>
namespace irr::video namespace irr::video
{ {
@ -26,6 +27,10 @@ namespace irr::video
*/ */
void imageCleanTransparent(video::IImage *src, u32 threshold); void imageCleanTransparent(video::IImage *src, u32 threshold);
/* Returns the gamma-correct average color of the image, with transparent pixels
* ignored. */
video::SColor imageAverageColor(const video::IImage *img);
/* Scale a region of an image into another image, using nearest-neighbor with /* Scale a region of an image into another image, using nearest-neighbor with
* anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries * anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
* to prevent non-integer scaling ratio artifacts. Note that this may cause * to prevent non-integer scaling ratio artifacts. Note that this may cause

View file

@ -31,8 +31,7 @@ void SourceImageCache::insert(const std::string &name, video::IImage *img, bool
{ {
assert(img); // Pre-condition assert(img); // Pre-condition
// Remove old image // Remove old image
std::map<std::string, video::IImage*>::iterator n; auto n = m_images.find(name);
n = m_images.find(name);
if (n != m_images.end()){ if (n != m_images.end()){
if (n->second) if (n->second)
n->second->drop(); n->second->drop();
@ -63,8 +62,7 @@ void SourceImageCache::insert(const std::string &name, video::IImage *img, bool
video::IImage* SourceImageCache::get(const std::string &name) video::IImage* SourceImageCache::get(const std::string &name)
{ {
std::map<std::string, video::IImage*>::iterator n; auto n = m_images.find(name);
n = m_images.find(name);
if (n != m_images.end()) if (n != m_images.end())
return n->second; return n->second;
return nullptr; return nullptr;
@ -73,8 +71,7 @@ video::IImage* SourceImageCache::get(const std::string &name)
// Primarily fetches from cache, secondarily tries to read from filesystem // Primarily fetches from cache, secondarily tries to read from filesystem
video::IImage* SourceImageCache::getOrLoad(const std::string &name) video::IImage* SourceImageCache::getOrLoad(const std::string &name)
{ {
std::map<std::string, video::IImage*>::iterator n; auto n = m_images.find(name);
n = m_images.find(name);
if (n != m_images.end()){ if (n != m_images.end()){
n->second->grab(); // Grab for caller n->second->grab(); // Grab for caller
return n->second; return n->second;
@ -166,13 +163,13 @@ static void draw_crack(video::IImage *crack, video::IImage *dst,
video::IVideoDriver *driver, u8 tiles = 1); video::IVideoDriver *driver, u8 tiles = 1);
// Brighten image // Brighten image
void brighten(video::IImage *image); static void brighten(video::IImage *image);
// Parse a transform name // Parse a transform name
u32 parseImageTransform(std::string_view s); static u32 parseImageTransform(std::string_view s);
// Apply transform to image dimension // Apply transform to image dimension
core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim); static core::dimension2du imageTransformDimension(u32 transform, core::dimension2du dim);
// Apply transform to image data // Apply transform to image data
void imageTransform(u32 transform, video::IImage *src, video::IImage *dst); static void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
inline static void applyShadeFactor(video::SColor &color, u32 factor) inline static void applyShadeFactor(video::SColor &color, u32 factor)
{ {
@ -289,7 +286,7 @@ static video::IImage *createInventoryCubeImage(
return result; return result;
} }
static std::string unescape_string(const std::string &str, const char esc = '\\') static std::string unescape_string(std::string_view str, const char esc = '\\')
{ {
std::string out; std::string out;
size_t pos = 0, cpos; size_t pos = 0, cpos;
@ -300,7 +297,8 @@ static std::string unescape_string(const std::string &str, const char esc = '\\'
out += str.substr(pos); out += str.substr(pos);
break; break;
} }
out += str.substr(pos, cpos - pos) + str[cpos + 1]; out += str.substr(pos, cpos - pos);
out += str[cpos + 1];
pos = cpos + 2; pos = cpos + 2;
} }
return out; return out;
@ -312,7 +310,7 @@ static std::string unescape_string(const std::string &str, const char esc = '\\'
Ensure no other references to these images are being held, as one may Ensure no other references to these images are being held, as one may
get dropped and switched with a new image. get dropped and switched with a new image.
*/ */
void upscaleImagesToMatchLargest(video::IImage *& img1, static void upscaleImagesToMatchLargest(video::IImage *& img1,
video::IImage *& img2) video::IImage *& img2)
{ {
core::dimension2d<u32> dim1 = img1->getDimension(); core::dimension2d<u32> dim1 = img1->getDimension();
@ -340,7 +338,7 @@ void upscaleImagesToMatchLargest(video::IImage *& img1,
} }
} }
void blitBaseImage(video::IImage* &src, video::IImage* &dst) static void blitBaseImage(video::IImage* &src, video::IImage* &dst)
{ {
//infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl; //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
upscaleImagesToMatchLargest(dst, src); upscaleImagesToMatchLargest(dst, src);
@ -411,9 +409,10 @@ void blit_pixel(video::SColor src_col, video::SColor &dst_col)
dst_col.set(dst_a, dst.r, dst.g, dst.b); dst_col.set(dst_a, dst.r, dst.g, dst.b);
} }
} // namespace } // namespace (anonymous)
template<bool overlay> template<bool overlay>
void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 dst_pos, static void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 dst_pos,
v2u32 size) v2u32 size)
{ {
if (dst->getColorFormat() != video::ECF_A8R8G8B8) if (dst->getColorFormat() != video::ECF_A8R8G8B8)
@ -427,13 +426,12 @@ void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 dst_pos,
video::IVideoDriver *driver = RenderingEngine::get_video_driver(); video::IVideoDriver *driver = RenderingEngine::get_video_driver();
video::IImage *src_converted = driver->createImage(video::ECF_A8R8G8B8, video::IImage *src_converted = driver->createImage(video::ECF_A8R8G8B8,
src_dim); src_dim);
if (!src_converted) sanity_check(src_converted != nullptr);
throw BaseException("blit_with_alpha() failed to convert the "
"source image to ECF_A8R8G8B8.");
src->copyTo(src_converted); src->copyTo(src_converted);
src = src_converted; src = src_converted;
drop_src = true; drop_src = true;
} }
video::SColor *pixels_src = video::SColor *pixels_src =
reinterpret_cast<video::SColor *>(src->getData()); reinterpret_cast<video::SColor *>(src->getData());
video::SColor *pixels_dst = video::SColor *pixels_dst =
@ -453,6 +451,7 @@ void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 dst_pos,
blit_pixel<overlay>(pixels_src[i_src++], pixels_dst[i_dst++]); blit_pixel<overlay>(pixels_src[i_src++], pixels_dst[i_dst++]);
} }
} }
if (drop_src) if (drop_src)
src->drop(); src->drop();
} }
@ -726,7 +725,7 @@ static void apply_mask(video::IImage *mask, video::IImage *dst,
} }
} }
video::IImage *create_crack_image(video::IImage *crack, s32 frame_index, static video::IImage *create_crack_image(video::IImage *crack, s32 frame_index,
core::dimension2d<u32> size, u8 tiles, video::IVideoDriver *driver) core::dimension2d<u32> size, u8 tiles, video::IVideoDriver *driver)
{ {
core::dimension2d<u32> strip_size = crack->getDimension(); core::dimension2d<u32> strip_size = crack->getDimension();
@ -804,7 +803,7 @@ static void draw_crack(video::IImage *crack, video::IImage *dst,
crack_scaled->drop(); crack_scaled->drop();
} }
void brighten(video::IImage *image) static void brighten(video::IImage *image)
{ {
if (image == NULL) if (image == NULL)
return; return;
@ -822,7 +821,7 @@ void brighten(video::IImage *image)
} }
} }
u32 parseImageTransform(std::string_view s) static u32 parseImageTransform(std::string_view s)
{ {
int total_transform = 0; int total_transform = 0;
@ -872,15 +871,15 @@ u32 parseImageTransform(std::string_view s)
return total_transform; return total_transform;
} }
core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim) static core::dimension2du imageTransformDimension(u32 transform, core::dimension2du dim)
{ {
if (transform % 2 == 0) if (transform % 2 == 0)
return dim; return dim;
return core::dimension2d<u32>(dim.Height, dim.Width); return core::dimension2du(dim.Height, dim.Width);
} }
void imageTransform(u32 transform, video::IImage *src, video::IImage *dst) static void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
{ {
if (src == NULL || dst == NULL) if (src == NULL || dst == NULL)
return; return;
@ -925,48 +924,6 @@ void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
} }
} }
namespace {
// For more colorspace transformations, see for example
// https://github.com/tobspr/GLSL-Color-Spaces/blob/master/ColorSpaces.inc.glsl
inline float linear_to_srgb_component(float v)
{
if (v > 0.0031308f)
return 1.055f * powf(v, 1.0f / 2.4f) - 0.055f;
return 12.92f * v;
}
inline float srgb_to_linear_component(float v)
{
if (v > 0.04045f)
return powf((v + 0.055f) / 1.055f, 2.4f);
return v / 12.92f;
}
v3f srgb_to_linear(const video::SColor col_srgb)
{
v3f col(col_srgb.getRed(), col_srgb.getGreen(), col_srgb.getBlue());
col /= 255.0f;
col.X = srgb_to_linear_component(col.X);
col.Y = srgb_to_linear_component(col.Y);
col.Z = srgb_to_linear_component(col.Z);
return col;
}
video::SColor linear_to_srgb(const v3f col_linear)
{
v3f col;
col.X = linear_to_srgb_component(col_linear.X);
col.Y = linear_to_srgb_component(col_linear.Y);
col.Z = linear_to_srgb_component(col_linear.Z);
col *= 255.0f;
col.X = core::clamp<float>(col.X, 0.0f, 255.0f);
col.Y = core::clamp<float>(col.Y, 0.0f, 255.0f);
col.Z = core::clamp<float>(col.Z, 0.0f, 255.0f);
return video::SColor(0xff, myround(col.X), myround(col.Y),
myround(col.Z));
}
}
/////////////////////////// ///////////////////////////
// ImageSource Functions // // ImageSource Functions //
@ -1017,18 +974,12 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
std::string part_s(part_of_name); std::string part_s(part_of_name);
source_image_names.insert(part_s); source_image_names.insert(part_s);
video::IImage *image = m_sourcecache.getOrLoad(part_s); video::IImage *image = m_sourcecache.getOrLoad(part_s);
if (!image) { if (!image) {
// Do not create the dummy texture // Do not create the dummy texture
if (part_of_name.empty()) if (part_of_name.empty())
return true; return true;
// Do not create normalmap dummies
if (str_ends_with(part_of_name, "_normal.png")) {
warningstream << "generateImagePart(): Could not load normal map \""
<< part_of_name << "\"" << std::endl;
return true;
}
errorstream << "generateImagePart(): Could not load image \"" errorstream << "generateImagePart(): Could not load image \""
<< part_of_name << "\" while building texture; " << part_of_name << "\" while building texture; "
"Creating a dummy image" << std::endl; "Creating a dummy image" << std::endl;
@ -1040,16 +991,15 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
myrand()%256,myrand()%256)); myrand()%256,myrand()%256));
} }
// If base image is NULL, load as base. // load as base or blit
if (baseimg == NULL) if (!baseimg)
{ {
/* /*
Copy it this way to get an alpha channel. Copy it this way to get an alpha channel.
Otherwise images with alpha cannot be blitted on Otherwise images with alpha cannot be blitted on
images that don't have alpha in the original file. images that don't have alpha in the original file.
*/ */
core::dimension2d<u32> dim = image->getDimension(); baseimg = driver->createImage(video::ECF_A8R8G8B8, image->getDimension());
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
image->copyTo(baseimg); image->copyTo(baseimg);
} }
// Else blit on base. // Else blit on base.
@ -1705,14 +1655,17 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
return false; return false;
} }
// blit or use as base
if (baseimg) { if (baseimg) {
blitBaseImage(pngimg, baseimg); blitBaseImage(pngimg, baseimg);
} else { pngimg->drop();
core::dimension2d<u32> dim = pngimg->getDimension(); } else if (pngimg->getColorFormat() != video::ECF_A8R8G8B8) {
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); baseimg = driver->createImage(video::ECF_A8R8G8B8, pngimg->getDimension());
pngimg->copyTo(baseimg); pngimg->copyTo(baseimg);
pngimg->drop();
} else {
baseimg = pngimg;
} }
pngimg->drop();
} }
/* /*
[hsl:hue:saturation:lightness [hsl:hue:saturation:lightness
@ -1945,32 +1898,7 @@ video::IImage* ImageSource::generateImage(std::string_view name,
return baseimg; return baseimg;
} }
video::SColor ImageSource::getImageAverageColor(const video::IImage &image) void ImageSource::insertSourceImage(const std::string &name, video::IImage *img, bool prefer_local)
{ {
video::SColor c(0, 0, 0, 0);
u32 total = 0;
v3f col_acc(0, 0, 0);
core::dimension2d<u32> dim = image.getDimension();
u16 step = 1;
if (dim.Width > 16)
step = dim.Width / 16;
for (u16 x = 0; x < dim.Width; x += step) {
for (u16 y = 0; y < dim.Width; y += step) {
c = image.getPixel(x,y);
if (c.getAlpha() > 0) {
total++;
col_acc += srgb_to_linear(c);
}
}
}
if (total > 0) {
col_acc /= total;
c = linear_to_srgb(col_acc);
}
c.setAlpha(255);
return c;
}
void ImageSource::insertSourceImage(const std::string &name, video::IImage *img, bool prefer_local) {
m_sourcecache.insert(name, img, prefer_local); m_sourcecache.insert(name, img, prefer_local);
} }

View file

@ -5,7 +5,7 @@
#pragma once #pragma once
#include <IImage.h> #include <IImage.h>
#include <map> #include <unordered_map>
#include <set> #include <set>
#include <string> #include <string>
@ -28,7 +28,7 @@ public:
// Primarily fetches from cache, secondarily tries to read from filesystem. // Primarily fetches from cache, secondarily tries to read from filesystem.
video::IImage *getOrLoad(const std::string &name); video::IImage *getOrLoad(const std::string &name);
private: private:
std::map<std::string, video::IImage*> m_images; std::unordered_map<std::string, video::IImage*> m_images;
}; };
// Generates images using texture modifiers, and caches source images. // Generates images using texture modifiers, and caches source images.
@ -45,9 +45,6 @@ struct ImageSource {
// Insert a source image into the cache without touching the filesystem. // Insert a source image into the cache without touching the filesystem.
void insertSourceImage(const std::string &name, video::IImage *img, bool prefer_local); void insertSourceImage(const std::string &name, video::IImage *img, bool prefer_local);
// TODO should probably be moved elsewhere
static video::SColor getImageAverageColor(const video::IImage &image);
private: private:
// Generate image based on a string like "stone.png" or "[crack:1:0". // Generate image based on a string like "stone.png" or "[crack:1:0".

View file

@ -75,10 +75,8 @@ static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
if (nodeboxes.empty()) if (nodeboxes.empty())
return aabb3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); return aabb3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
aabb3f b_max; auto it = nodeboxes.begin();
aabb3f b_max(it->MinEdge, it->MaxEdge);
std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
b_max = aabb3f(it->MinEdge, it->MaxEdge);
++it; ++it;
for (; it != nodeboxes.end(); ++it) for (; it != nodeboxes.end(); ++it)

View file

@ -633,8 +633,7 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs
*/ */
{ {
MapblockMeshGenerator(data, &collector, MapblockMeshGenerator(data, &collector).generate();
client->getSceneManager()->getMeshManipulator()).generate();
} }
/* /*

View file

@ -105,8 +105,7 @@ void scaleMesh(scene::IMesh *mesh, v3f scale)
if (mesh == NULL) if (mesh == NULL)
return; return;
aabb3f bbox; aabb3f bbox{{0.0f, 0.0f, 0.0f}};
bbox.reset(0, 0, 0);
u32 mc = mesh->getMeshBufferCount(); u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; j++) { for (u32 j = 0; j < mc; j++) {
@ -134,8 +133,7 @@ void translateMesh(scene::IMesh *mesh, v3f vec)
if (mesh == NULL) if (mesh == NULL)
return; return;
aabb3f bbox; aabb3f bbox{{0.0f, 0.0f, 0.0f}};
bbox.reset(0, 0, 0);
u32 mc = mesh->getMeshBufferCount(); u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; j++) { for (u32 j = 0; j < mc; j++) {
@ -296,8 +294,7 @@ void rotateMeshBy6dFacedir(scene::IMesh *mesh, u8 facedir)
void recalculateBoundingBox(scene::IMesh *src_mesh) void recalculateBoundingBox(scene::IMesh *src_mesh)
{ {
aabb3f bbox; aabb3f bbox{{0.0f, 0.0f, 0.0f}};
bbox.reset(0,0,0);
for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) { for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j); scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
buf->recalculateBoundingBox(); buf->recalculateBoundingBox();

View file

@ -619,15 +619,22 @@ const core::aabbox3df &ParticleBuffer::getBoundingBox() const
if (!m_bounding_box_dirty) if (!m_bounding_box_dirty)
return m_mesh_buffer->BoundingBox; return m_mesh_buffer->BoundingBox;
core::aabbox3df box; core::aabbox3df box{{0, 0, 0}};
bool first = true;
for (u16 i = 0; i < m_count; i++) { for (u16 i = 0; i < m_count; i++) {
// check if this index is used // check if this index is used
static_assert(quad_indices[1] != 0); static_assert(quad_indices[1] != 0);
if (m_mesh_buffer->getIndices()[6 * i + 1] == 0) if (m_mesh_buffer->getIndices()[6 * i + 1] == 0)
continue; continue;
for (u16 j = 0; j < 4; j++) for (u16 j = 0; j < 4; j++) {
box.addInternalPoint(m_mesh_buffer->getPosition(i * 4 + j)); const auto pos = m_mesh_buffer->getPosition(i * 4 + j);
if (first)
box.reset(pos);
else
box.addInternalPoint(pos);
first = false;
}
} }
m_mesh_buffer->BoundingBox = box; m_mesh_buffer->BoundingBox = box;

View file

@ -135,7 +135,7 @@ static irr::IrrlichtDevice *createDevice(SIrrlichtCreationParameters params, std
{ {
if (requested_driver) { if (requested_driver) {
params.DriverType = *requested_driver; params.DriverType = *requested_driver;
verbosestream << "Trying video driver " << getVideoDriverName(params.DriverType) << std::endl; infostream << "Trying video driver " << getVideoDriverName(params.DriverType) << std::endl;
if (auto *device = createDeviceEx(params)) if (auto *device = createDeviceEx(params))
return device; return device;
errorstream << "Failed to initialize the " << getVideoDriverName(params.DriverType) << " video driver" << std::endl; errorstream << "Failed to initialize the " << getVideoDriverName(params.DriverType) << " video driver" << std::endl;
@ -147,7 +147,7 @@ static irr::IrrlichtDevice *createDevice(SIrrlichtCreationParameters params, std
if (fallback_driver == video::EDT_NULL || fallback_driver == requested_driver) if (fallback_driver == video::EDT_NULL || fallback_driver == requested_driver)
continue; continue;
params.DriverType = fallback_driver; params.DriverType = fallback_driver;
verbosestream << "Trying video driver " << getVideoDriverName(params.DriverType) << std::endl; infostream << "Trying video driver " << getVideoDriverName(params.DriverType) << std::endl;
if (auto *device = createDeviceEx(params)) if (auto *device = createDeviceEx(params))
return device; return device;
} }
@ -374,16 +374,16 @@ void RenderingEngine::draw_load_screen(const std::wstring &text,
std::vector<video::E_DRIVER_TYPE> RenderingEngine::getSupportedVideoDrivers() std::vector<video::E_DRIVER_TYPE> RenderingEngine::getSupportedVideoDrivers()
{ {
// Only check these drivers. We do not support software and D3D in any capacity. // Only check these drivers. We do not support software and D3D in any capacity.
// Order by preference (best first) // ordered by preference (best first)
static const video::E_DRIVER_TYPE glDrivers[] = { static const video::E_DRIVER_TYPE glDrivers[] = {
video::EDT_OPENGL,
video::EDT_OPENGL3, video::EDT_OPENGL3,
video::EDT_OPENGL,
video::EDT_OGLES2, video::EDT_OGLES2,
video::EDT_NULL, video::EDT_NULL,
}; };
std::vector<video::E_DRIVER_TYPE> drivers; std::vector<video::E_DRIVER_TYPE> drivers;
for (video::E_DRIVER_TYPE driver: glDrivers) { for (auto driver : glDrivers) {
if (IrrlichtDevice::isDriverSupported(driver)) if (IrrlichtDevice::isDriverSupported(driver))
drivers.push_back(driver); drivers.push_back(driver);
} }

View file

@ -19,7 +19,6 @@
#include <IMaterialRendererServices.h> #include <IMaterialRendererServices.h>
#include <IShaderConstantSetCallBack.h> #include <IShaderConstantSetCallBack.h>
#include "client/renderingengine.h" #include "client/renderingengine.h"
#include <EShaderTypes.h>
#include "gettext.h" #include "gettext.h"
#include "log.h" #include "log.h"
#include "gamedef.h" #include "gamedef.h"
@ -615,10 +614,16 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
#define textureFlags texture2 #define textureFlags texture2
)"; )";
/// Unique name of this shader, for debug/logging
std::string log_name = name;
/* Define constants for node and object shaders */ /* Define constants for node and object shaders */
const bool node_shader = drawtype != NodeDrawType_END; const bool node_shader = drawtype != NodeDrawType_END;
if (node_shader) { if (node_shader) {
log_name.append(" mat=").append(itos(material_type))
.append(" draw=").append(itos(drawtype));
bool use_discard = fully_programmable; bool use_discard = fully_programmable;
if (!use_discard) { if (!use_discard) {
// workaround for a certain OpenGL implementation lacking GL_ALPHA_TEST // workaround for a certain OpenGL implementation lacking GL_ALPHA_TEST
@ -775,18 +780,15 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
geometry_shader_ptr = geometry_shader.c_str(); geometry_shader_ptr = geometry_shader.c_str();
} }
irr_ptr<ShaderCallback> cb{new ShaderCallback(m_setter_factories)}; auto cb = make_irr<ShaderCallback>(m_setter_factories);
infostream<<"Compiling high level shaders for "<<name<<std::endl; infostream << "Compiling high level shaders for " << log_name << std::endl;
s32 shadermat = gpu->addHighLevelShaderMaterial( s32 shadermat = gpu->addHighLevelShaderMaterial(
vertex_shader.c_str(), nullptr, video::EVST_VS_1_1, vertex_shader.c_str(), fragment_shader.c_str(), geometry_shader_ptr,
fragment_shader.c_str(), nullptr, video::EPST_PS_1_1, log_name.c_str(), scene::EPT_TRIANGLES, scene::EPT_TRIANGLES, 0,
geometry_shader_ptr, nullptr, video::EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLES, 0, cb.get(), shaderinfo.base_material);
cb.get(), shaderinfo.base_material, 1);
if (shadermat == -1) { if (shadermat == -1) {
errorstream<<"generate_shader(): " errorstream << "generateShader(): failed to generate shaders for "
"failed to generate \""<<name<<"\", " << log_name << ", addHighLevelShaderMaterial failed." << std::endl;
"addHighLevelShaderMaterial failed."
<<std::endl;
dumpShaderProgram(warningstream, "Vertex", vertex_shader); dumpShaderProgram(warningstream, "Vertex", vertex_shader);
dumpShaderProgram(warningstream, "Fragment", fragment_shader); dumpShaderProgram(warningstream, "Fragment", fragment_shader);
dumpShaderProgram(warningstream, "Geometry", geometry_shader); dumpShaderProgram(warningstream, "Geometry", geometry_shader);

View file

@ -14,10 +14,9 @@
#include "client/client.h" #include "client/client.h"
#include "client/clientmap.h" #include "client/clientmap.h"
#include "profiler.h" #include "profiler.h"
#include "EShaderTypes.h"
#include "IGPUProgrammingServices.h" #include "IGPUProgrammingServices.h"
#include "IMaterialRenderer.h" #include "IMaterialRenderer.h"
#include <IVideoDriver.h> #include "IVideoDriver.h"
ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) : ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) :
m_smgr(device->getSceneManager()), m_driver(device->getVideoDriver()), m_smgr(device->getSceneManager()), m_driver(device->getVideoDriver()),
@ -539,10 +538,9 @@ void ShadowRenderer::createShaders()
m_shadow_depth_cb = new ShadowDepthShaderCB(); m_shadow_depth_cb = new ShadowDepthShaderCB();
depth_shader = gpu->addHighLevelShaderMaterial( depth_shader = gpu->addHighLevelShaderMaterial(
readShaderFile(depth_shader_vs).c_str(), "vertexMain", readShaderFile(depth_shader_vs).c_str(),
video::EVST_VS_1_1, readShaderFile(depth_shader_fs).c_str(), nullptr,
readShaderFile(depth_shader_fs).c_str(), "pixelMain", m_shadow_depth_cb, video::EMT_ONETEXTURE_BLEND);
video::EPST_PS_1_2, m_shadow_depth_cb, video::EMT_ONETEXTURE_BLEND);
if (depth_shader == -1) { if (depth_shader == -1) {
// upsi, something went wrong loading shader. // upsi, something went wrong loading shader.
@ -578,10 +576,9 @@ void ShadowRenderer::createShaders()
m_shadow_depth_entity_cb = new ShadowDepthShaderCB(); m_shadow_depth_entity_cb = new ShadowDepthShaderCB();
depth_shader_entities = gpu->addHighLevelShaderMaterial( depth_shader_entities = gpu->addHighLevelShaderMaterial(
readShaderFile(depth_shader_vs).c_str(), "vertexMain", readShaderFile(depth_shader_vs).c_str(),
video::EVST_VS_1_1, readShaderFile(depth_shader_fs).c_str(), nullptr,
readShaderFile(depth_shader_fs).c_str(), "pixelMain", m_shadow_depth_entity_cb);
video::EPST_PS_1_2, m_shadow_depth_entity_cb);
if (depth_shader_entities == -1) { if (depth_shader_entities == -1) {
// upsi, something went wrong loading shader. // upsi, something went wrong loading shader.
@ -616,10 +613,9 @@ void ShadowRenderer::createShaders()
m_shadow_mix_cb = new shadowScreenQuadCB(); m_shadow_mix_cb = new shadowScreenQuadCB();
m_screen_quad = new shadowScreenQuad(); m_screen_quad = new shadowScreenQuad();
mixcsm_shader = gpu->addHighLevelShaderMaterial( mixcsm_shader = gpu->addHighLevelShaderMaterial(
readShaderFile(depth_shader_vs).c_str(), "vertexMain", readShaderFile(depth_shader_vs).c_str(),
video::EVST_VS_1_1, readShaderFile(depth_shader_fs).c_str(), nullptr,
readShaderFile(depth_shader_fs).c_str(), "pixelMain", m_shadow_mix_cb);
video::EPST_PS_1_2, m_shadow_mix_cb);
m_screen_quad->getMaterial().MaterialType = m_screen_quad->getMaterial().MaterialType =
(video::E_MATERIAL_TYPE)mixcsm_shader; (video::E_MATERIAL_TYPE)mixcsm_shader;
@ -655,10 +651,9 @@ void ShadowRenderer::createShaders()
m_shadow_depth_trans_cb = new ShadowDepthShaderCB(); m_shadow_depth_trans_cb = new ShadowDepthShaderCB();
depth_shader_trans = gpu->addHighLevelShaderMaterial( depth_shader_trans = gpu->addHighLevelShaderMaterial(
readShaderFile(depth_shader_vs).c_str(), "vertexMain", readShaderFile(depth_shader_vs).c_str(),
video::EVST_VS_1_1, readShaderFile(depth_shader_fs).c_str(), nullptr,
readShaderFile(depth_shader_fs).c_str(), "pixelMain", m_shadow_depth_trans_cb);
video::EPST_PS_1_2, m_shadow_depth_trans_cb);
if (depth_shader_trans == -1) { if (depth_shader_trans == -1) {
// upsi, something went wrong loading shader. // upsi, something went wrong loading shader.

View file

@ -50,8 +50,6 @@ Sky::Sky(s32 id, RenderingEngine *rendering_engine, ITextureSource *tsrc, IShade
m_seed = (u64)myrand() << 32 | myrand(); m_seed = (u64)myrand() << 32 | myrand();
setAutomaticCulling(scene::EAC_OFF); setAutomaticCulling(scene::EAC_OFF);
m_box.MaxEdge.set(0, 0, 0);
m_box.MinEdge.set(0, 0, 0);
m_sky_params = SkyboxDefaults::getSkyDefaults(); m_sky_params = SkyboxDefaults::getSkyDefaults();
m_sun_params = SkyboxDefaults::getSunDefaults(); m_sun_params = SkyboxDefaults::getSunDefaults();

View file

@ -122,7 +122,7 @@ public:
} }
private: private:
aabb3f m_box; aabb3f m_box{{0.0f, 0.0f, 0.0f}};
video::SMaterial m_materials[SKY_MATERIAL_COUNT]; video::SMaterial m_materials[SKY_MATERIAL_COUNT];
// How much sun & moon transition should affect horizon color // How much sun & moon transition should affect horizon color
float m_horizon_blend() float m_horizon_blend()

View file

@ -121,7 +121,6 @@ public:
// Shall be called from the main thread. // Shall be called from the main thread.
void rebuildImagesAndTextures(); void rebuildImagesAndTextures();
video::ITexture* getNormalTexture(const std::string &name);
video::SColor getTextureAverageColor(const std::string &name); video::SColor getTextureAverageColor(const std::string &name);
private: private:
@ -488,40 +487,20 @@ void TextureSource::rebuildTexture(video::IVideoDriver *driver, TextureInfo &ti)
m_texture_trash.push_back(t_old); m_texture_trash.push_back(t_old);
} }
video::ITexture* TextureSource::getNormalTexture(const std::string &name)
{
if (isKnownSourceImage("override_normal.png"))
return getTexture("override_normal.png");
std::string fname_base = name;
static const char *normal_ext = "_normal.png";
static const u32 normal_ext_size = strlen(normal_ext);
size_t pos = fname_base.find('.');
std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
if (isKnownSourceImage(fname_normal)) {
// look for image extension and replace it
size_t i = 0;
while ((i = fname_base.find('.', i)) != std::string::npos) {
fname_base.replace(i, 4, normal_ext);
i += normal_ext_size;
}
return getTexture(fname_base);
}
return nullptr;
}
video::SColor TextureSource::getTextureAverageColor(const std::string &name) video::SColor TextureSource::getTextureAverageColor(const std::string &name)
{ {
video::IVideoDriver *driver = RenderingEngine::get_video_driver(); video::IVideoDriver *driver = RenderingEngine::get_video_driver();
video::ITexture *texture = getTexture(name); video::ITexture *texture = getTexture(name);
if (!texture) if (!texture)
return {0, 0, 0, 0}; return {0, 0, 0, 0};
// Note: this downloads the texture back from the GPU, which is pointless
video::IImage *image = driver->createImage(texture, video::IImage *image = driver->createImage(texture,
core::position2d<s32>(0, 0), core::position2d<s32>(0, 0),
texture->getOriginalSize()); texture->getOriginalSize());
if (!image) if (!image)
return {0, 0, 0, 0}; return {0, 0, 0, 0};
video::SColor c = ImageSource::getImageAverageColor(*image); video::SColor c = imageAverageColor(image);
image->drop(); image->drop();
return c; return c;

View file

@ -54,7 +54,6 @@ public:
*/ */
virtual Palette* getPalette(const std::string &name) = 0; virtual Palette* getPalette(const std::string &name) = 0;
virtual bool isKnownSourceImage(const std::string &name)=0; virtual bool isKnownSourceImage(const std::string &name)=0;
virtual video::ITexture* getNormalTexture(const std::string &name)=0;
virtual video::SColor getTextureAverageColor(const std::string &name)=0; virtual video::SColor getTextureAverageColor(const std::string &name)=0;
}; };
@ -75,7 +74,6 @@ public:
virtual void processQueue()=0; virtual void processQueue()=0;
virtual void insertSourceImage(const std::string &name, video::IImage *img)=0; virtual void insertSourceImage(const std::string &name, video::IImage *img)=0;
virtual void rebuildImagesAndTextures()=0; virtual void rebuildImagesAndTextures()=0;
virtual video::ITexture* getNormalTexture(const std::string &name)=0;
virtual video::SColor getTextureAverageColor(const std::string &name)=0; virtual video::SColor getTextureAverageColor(const std::string &name)=0;
}; };

View file

@ -83,10 +83,13 @@ struct TileLayer
void applyMaterialOptionsWithShaders(video::SMaterial &material) const; void applyMaterialOptionsWithShaders(video::SMaterial &material) const;
/// @return is this layer semi-transparent?
bool isTransparent() const bool isTransparent() const
{ {
// see also: the mapping in ShaderSource::generateShader()
switch (material_type) { switch (material_type) {
case TILE_MATERIAL_ALPHA: case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_PLAIN_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT: case TILE_MATERIAL_LIQUID_TRANSPARENT:
case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT: case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
return true; return true;

View file

@ -195,8 +195,7 @@ WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id):
else else
g_extrusion_mesh_cache->grab(); g_extrusion_mesh_cache->grab();
// Disable bounding box culling for this scene node // This class doesn't render anything, so disable culling.
// since we won't calculate the bounding box.
setAutomaticCulling(scene::EAC_OFF); setAutomaticCulling(scene::EAC_OFF);
// Create the child scene node // Create the child scene node
@ -299,8 +298,7 @@ static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n,
MeshMakeData mesh_make_data(client->ndef(), 1); MeshMakeData mesh_make_data(client->ndef(), 1);
MeshCollector collector(v3f(0.0f * BS), v3f()); MeshCollector collector(v3f(0.0f * BS), v3f());
mesh_make_data.setSmoothLighting(false); mesh_make_data.setSmoothLighting(false);
MapblockMeshGenerator gen(&mesh_make_data, &collector, MapblockMeshGenerator gen(&mesh_make_data, &collector);
client->getSceneManager()->getMeshManipulator());
if (n.getParam2()) { if (n.getParam2()) {
// keep it // keep it

View file

@ -134,7 +134,7 @@ private:
// Bounding box culling is disabled for this type of scene node, // Bounding box culling is disabled for this type of scene node,
// so this variable is just required so we can implement // so this variable is just required so we can implement
// getBoundingBox() and is set to an empty box. // getBoundingBox() and is set to an empty box.
aabb3f m_bounding_box; aabb3f m_bounding_box{{0, 0, 0}};
ShadowRenderer *m_shadow; ShadowRenderer *m_shadow;
}; };

View file

@ -262,7 +262,7 @@ static void add_object_boxes(Environment *env,
{ {
auto process_object = [&cinfo] (ActiveObject *object) { auto process_object = [&cinfo] (ActiveObject *object) {
if (object && object->collideWithObjects()) { if (object && object->collideWithObjects()) {
aabb3f box; aabb3f box{{0.0f, 0.0f, 0.0f}};
if (object->getCollisionBox(&box)) if (object->getCollisionBox(&box))
cinfo.emplace_back(object, 0, box); cinfo.emplace_back(object, 0, box);
} }

View file

@ -104,6 +104,7 @@ void set_default_settings()
settings->setDefault("sound_extensions_blacklist", ""); settings->setDefault("sound_extensions_blacklist", "");
settings->setDefault("mesh_generation_interval", "0"); settings->setDefault("mesh_generation_interval", "0");
settings->setDefault("mesh_generation_threads", "0"); settings->setDefault("mesh_generation_threads", "0");
settings->setDefault("mesh_buffer_min_vertices", "100");
settings->setDefault("free_move", "false"); settings->setDefault("free_move", "false");
settings->setDefault("pitch_move", "false"); settings->setDefault("pitch_move", "false");
settings->setDefault("fast_move", "false"); settings->setDefault("fast_move", "false");
@ -311,7 +312,7 @@ void set_default_settings()
// Effects // Effects
settings->setDefault("enable_post_processing", "true"); settings->setDefault("enable_post_processing", "true");
settings->setDefault("post_processing_texture_bits", "16"); settings->setDefault("post_processing_texture_bits", "10");
settings->setDefault("directional_colored_fog", "true"); settings->setDefault("directional_colored_fog", "true");
settings->setDefault("inventory_items_animations", "false"); settings->setDefault("inventory_items_animations", "false");
settings->setDefault("mip_map", "false"); settings->setDefault("mip_map", "false");

View file

@ -194,9 +194,9 @@ void GUIEditBoxWithScrollBar::draw()
mbegin = font->getDimension(s.c_str()).Width; mbegin = font->getDimension(s.c_str()).Width;
// deal with kerning // deal with kerning
mbegin += font->getKerningWidth( mbegin += font->getKerning(
&((*txt_line)[realmbgn - start_pos]), (*txt_line)[realmbgn - start_pos],
realmbgn - start_pos > 0 ? &((*txt_line)[realmbgn - start_pos - 1]) : 0); realmbgn - start_pos > 0 ? (*txt_line)[realmbgn - start_pos - 1] : 0).X;
lineStartPos = realmbgn - start_pos; lineStartPos = realmbgn - start_pos;
} }
@ -242,7 +242,8 @@ void GUIEditBoxWithScrollBar::draw()
} }
s = txt_line->subString(0, m_cursor_pos - start_pos); s = txt_line->subString(0, m_cursor_pos - start_pos);
charcursorpos = font->getDimension(s.c_str()).Width + charcursorpos = font->getDimension(s.c_str()).Width +
font->getKerningWidth(L"_", m_cursor_pos - start_pos > 0 ? &((*txt_line)[m_cursor_pos - start_pos - 1]) : 0); font->getKerning(L'_',
m_cursor_pos - start_pos > 0 ? (*txt_line)[m_cursor_pos - start_pos - 1] : 0).X;
if (focus && (porting::getTimeMs() - m_blink_start_time) % 700 < 350) { if (focus && (porting::getTimeMs() - m_blink_start_time) % 700 < 350) {
setTextRect(cursor_line); setTextRect(cursor_line);
@ -431,7 +432,7 @@ void GUIEditBoxWithScrollBar::setTextRect(s32 line)
d = font->getDimension(Text.c_str()); d = font->getDimension(Text.c_str());
d.Height = AbsoluteRect.getHeight(); d.Height = AbsoluteRect.getHeight();
} }
d.Height += font->getKerningHeight(); d.Height += font->getKerning(L'A').Y;
// justification // justification
switch (m_halign) { switch (m_halign) {
@ -536,7 +537,7 @@ void GUIEditBoxWithScrollBar::calculateScrollPos()
// calculate vertical scrolling // calculate vertical scrolling
if (has_broken_text) { if (has_broken_text) {
irr::u32 line_height = font->getDimension(L"A").Height + font->getKerningHeight(); irr::u32 line_height = font->getDimension(L"A").Height + font->getKerning(L'A').Y;
// only up to 1 line fits? // only up to 1 line fits?
if (line_height >= (irr::u32)m_frame_rect.getHeight()) { if (line_height >= (irr::u32)m_frame_rect.getHeight()) {
m_vscroll_pos = 0; m_vscroll_pos = 0;

View file

@ -79,7 +79,7 @@
*/ */
static unsigned int font_line_height(gui::IGUIFont *font) static unsigned int font_line_height(gui::IGUIFont *font)
{ {
return font->getDimension(L"Ay").Height + font->getKerningHeight(); return font->getDimension(L"Ay").Height + font->getKerning(L'A').Y;
} }
inline u32 clamp_u8(s32 value) inline u32 clamp_u8(s32 value)

View file

@ -85,8 +85,8 @@ void GUIInventoryList::draw()
v2s32 p((i % m_geom.X) * m_slot_spacing.X, v2s32 p((i % m_geom.X) * m_slot_spacing.X,
(i / m_geom.X) * m_slot_spacing.Y); (i / m_geom.X) * m_slot_spacing.Y);
core::rect<s32> rect = imgrect + base_pos + p; core::rect<s32> rect = imgrect + base_pos + p;
ItemStack item = ilist->getItem(item_i); const ItemStack &orig_item = ilist->getItem(item_i);
ItemStack orig_item = item; ItemStack item = orig_item;
bool selected = selected_item bool selected = selected_item
&& m_invmgr->getInventory(selected_item->inventoryloc) == inv && m_invmgr->getInventory(selected_item->inventoryloc) == inv
@ -137,12 +137,26 @@ void GUIInventoryList::draw()
client, rotation_kind); client, rotation_kind);
} }
// Add hovering tooltip // Add hovering tooltip. The tooltip disappears if any item is selected,
// including the currently hovered one.
bool show_tooltip = !item.empty() && hovering && !selected_item; bool show_tooltip = !item.empty() && hovering && !selected_item;
// Make it possible to see item tooltips on touchscreens
if (RenderingEngine::getLastPointerType() == PointerType::Touch) { if (RenderingEngine::getLastPointerType() == PointerType::Touch) {
// Touchscreen users cannot hover over an item without selecting it.
// To allow touchscreen users to see item tooltips, we also show the
// tooltip if the item is selected and the finger is still on the
// source slot.
// The selected amount may be 0 in rare cases during "left-dragging"
// (used to distribute items evenly).
// In this case, the user doesn't see an item being dragged,
// so we don't show the tooltip.
// Note: `m_fs_menu->getSelectedAmount() != 0` below refers to the
// part of the selected item the user is dragging.
// `!item.empty()` would refer to the part of the selected item
// remaining in the source slot.
show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0; show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0;
} }
if (show_tooltip) { if (show_tooltip) {
std::string tooltip = orig_item.getDescription(client->idef()); std::string tooltip = orig_item.getDescription(client->idef());
if (m_fs_menu->doTooltipAppendItemname()) if (m_fs_menu->doTooltipAppendItemname())

View file

@ -29,52 +29,47 @@
TouchControls *g_touchcontrols; TouchControls *g_touchcontrols;
static void load_button_texture(IGUIImage *gui_button, const std::string &path, void TouchControls::emitKeyboardEvent(EKEY_CODE keycode, bool pressed)
const recti &button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver)
{ {
video::ITexture *texture = guiScalingImageButton(driver, SEvent e{};
tsrc->getTexture(path), button_rect.getWidth(), e.EventType = EET_KEY_INPUT_EVENT;
button_rect.getHeight()); e.KeyInput.Key = keycode;
e.KeyInput.Control = false;
e.KeyInput.Shift = false;
e.KeyInput.Char = 0;
e.KeyInput.PressedDown = pressed;
m_receiver->OnEvent(e);
}
void TouchControls::loadButtonTexture(IGUIImage *gui_button, const std::string &path)
{
auto rect = gui_button->getRelativePosition();
video::ITexture *texture = guiScalingImageButton(m_device->getVideoDriver(),
m_texturesource->getTexture(path), rect.getWidth(), rect.getHeight());
gui_button->setImage(texture); gui_button->setImage(texture);
gui_button->setScaleImage(true); gui_button->setScaleImage(true);
} }
void button_info::emitAction(bool action, video::IVideoDriver *driver, void TouchControls::buttonEmitAction(button_info &btn, bool action)
IEventReceiver *receiver, ISimpleTextureSource *tsrc)
{ {
if (keycode == KEY_UNKNOWN) if (btn.keycode == KEY_UNKNOWN)
return; return;
SEvent translated{}; emitKeyboardEvent(btn.keycode, action);
translated.EventType = EET_KEY_INPUT_EVENT;
translated.KeyInput.Key = keycode;
translated.KeyInput.Control = false;
translated.KeyInput.Shift = false;
translated.KeyInput.Char = 0;
if (action) { if (action) {
translated.KeyInput.PressedDown = true; if (btn.toggleable == button_info::FIRST_TEXTURE) {
receiver->OnEvent(translated); btn.toggleable = button_info::SECOND_TEXTURE;
loadButtonTexture(btn.gui_button.get(), btn.toggle_textures[1]);
if (toggleable == button_info::FIRST_TEXTURE) { } else if (btn.toggleable == button_info::SECOND_TEXTURE) {
toggleable = button_info::SECOND_TEXTURE; btn.toggleable = button_info::FIRST_TEXTURE;
load_button_texture(gui_button.get(), toggle_textures[1], loadButtonTexture(btn.gui_button.get(), btn.toggle_textures[0]);
gui_button->getRelativePosition(),
tsrc, driver);
} else if (toggleable == button_info::SECOND_TEXTURE) {
toggleable = button_info::FIRST_TEXTURE;
load_button_texture(gui_button.get(), toggle_textures[0],
gui_button->getRelativePosition(),
tsrc, driver);
} }
} else {
translated.KeyInput.PressedDown = false;
receiver->OnEvent(translated);
} }
} }
static bool buttons_handlePress(std::vector<button_info> &buttons, size_t pointer_id, IGUIElement *element, bool TouchControls::buttonsHandlePress(std::vector<button_info> &buttons, size_t pointer_id, IGUIElement *element)
video::IVideoDriver *driver, IEventReceiver *receiver, ISimpleTextureSource *tsrc)
{ {
if (!element) if (!element)
return false; return false;
@ -87,7 +82,7 @@ static bool buttons_handlePress(std::vector<button_info> &buttons, size_t pointe
if (btn.pointer_ids.size() > 1) if (btn.pointer_ids.size() > 1)
return true; return true;
btn.emitAction(true, driver, receiver, tsrc); buttonEmitAction(btn, true);
btn.repeat_counter = -BUTTON_REPEAT_DELAY; btn.repeat_counter = -BUTTON_REPEAT_DELAY;
return true; return true;
} }
@ -97,8 +92,7 @@ static bool buttons_handlePress(std::vector<button_info> &buttons, size_t pointe
} }
static bool buttons_handleRelease(std::vector<button_info> &buttons, size_t pointer_id, bool TouchControls::buttonsHandleRelease(std::vector<button_info> &buttons, size_t pointer_id)
video::IVideoDriver *driver, IEventReceiver *receiver, ISimpleTextureSource *tsrc)
{ {
for (button_info &btn : buttons) { for (button_info &btn : buttons) {
auto it = std::find(btn.pointer_ids.begin(), btn.pointer_ids.end(), pointer_id); auto it = std::find(btn.pointer_ids.begin(), btn.pointer_ids.end(), pointer_id);
@ -108,7 +102,7 @@ static bool buttons_handleRelease(std::vector<button_info> &buttons, size_t poin
if (!btn.pointer_ids.empty()) if (!btn.pointer_ids.empty())
return true; return true;
btn.emitAction(false, driver, receiver, tsrc); buttonEmitAction(btn, false);
return true; return true;
} }
} }
@ -116,8 +110,7 @@ static bool buttons_handleRelease(std::vector<button_info> &buttons, size_t poin
return false; return false;
} }
static bool buttons_step(std::vector<button_info> &buttons, float dtime, bool TouchControls::buttonsStep(std::vector<button_info> &buttons, float dtime)
video::IVideoDriver *driver, IEventReceiver *receiver, ISimpleTextureSource *tsrc)
{ {
bool has_pointers = false; bool has_pointers = false;
@ -130,8 +123,8 @@ static bool buttons_step(std::vector<button_info> &buttons, float dtime,
if (btn.repeat_counter < BUTTON_REPEAT_INTERVAL) if (btn.repeat_counter < BUTTON_REPEAT_INTERVAL)
continue; continue;
btn.emitAction(false, driver, receiver, tsrc); buttonEmitAction(btn, false);
btn.emitAction(true, driver, receiver, tsrc); buttonEmitAction(btn, true);
btn.repeat_counter = 0.0f; btn.repeat_counter = 0.0f;
} }
@ -340,8 +333,7 @@ void TouchControls::addButton(std::vector<button_info> &buttons, touch_gui_butto
{ {
IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id); IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id);
btn_gui_button->setVisible(visible); btn_gui_button->setVisible(visible);
load_button_texture(btn_gui_button, image, rect, loadButtonTexture(btn_gui_button, image);
m_texturesource, m_device->getVideoDriver());
button_info &btn = buttons.emplace_back(); button_info &btn = buttons.emplace_back();
btn.keycode = id_to_keycode(id); btn.keycode = id_to_keycode(id);
@ -363,8 +355,7 @@ IGUIImage *TouchControls::makeButtonDirect(touch_gui_button_id id,
{ {
IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id); IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id);
btn_gui_button->setVisible(visible); btn_gui_button->setVisible(visible);
load_button_texture(btn_gui_button, button_image_names[id], rect, loadButtonTexture(btn_gui_button, button_image_names[id]);
m_texturesource, m_device->getVideoDriver());
return btn_gui_button; return btn_gui_button;
} }
@ -399,11 +390,9 @@ void TouchControls::handleReleaseEvent(size_t pointer_id)
m_pointer_pos.erase(pointer_id); m_pointer_pos.erase(pointer_id);
// handle buttons // handle buttons
if (buttons_handleRelease(m_buttons, pointer_id, m_device->getVideoDriver(), if (buttonsHandleRelease(m_buttons, pointer_id))
m_receiver, m_texturesource))
return; return;
if (buttons_handleRelease(m_overflow_buttons, pointer_id, m_device->getVideoDriver(), if (buttonsHandleRelease(m_overflow_buttons, pointer_id))
m_receiver, m_texturesource))
return; return;
if (m_has_move_id && pointer_id == m_move_id) { if (m_has_move_id && pointer_id == m_move_id) {
@ -481,8 +470,7 @@ void TouchControls::translateEvent(const SEvent &event)
} }
} }
if (buttons_handlePress(m_overflow_buttons, pointer_id, element, if (buttonsHandlePress(m_overflow_buttons, pointer_id, element))
m_device->getVideoDriver(), m_receiver, m_texturesource))
return; return;
toggleOverflowMenu(); toggleOverflowMenu();
@ -494,8 +482,7 @@ void TouchControls::translateEvent(const SEvent &event)
} }
// handle buttons // handle buttons
if (buttons_handlePress(m_buttons, pointer_id, element, if (buttonsHandlePress(m_buttons, pointer_id, element))
m_device->getVideoDriver(), m_receiver, m_texturesource))
return; return;
// handle hotbar // handle hotbar
@ -614,16 +601,10 @@ void TouchControls::translateEvent(const SEvent &event)
void TouchControls::applyJoystickStatus() void TouchControls::applyJoystickStatus()
{ {
if (m_joystick_triggers_aux1) { if (m_joystick_triggers_aux1) {
SEvent translated{}; auto key = id_to_keycode(aux1_id);
translated.EventType = EET_KEY_INPUT_EVENT; emitKeyboardEvent(key, false);
translated.KeyInput.Key = id_to_keycode(aux1_id); if (m_joystick_status_aux1)
translated.KeyInput.PressedDown = false; emitKeyboardEvent(key, true);
m_receiver->OnEvent(translated);
if (m_joystick_status_aux1) {
translated.KeyInput.PressedDown = true;
m_receiver->OnEvent(translated);
}
} }
} }
@ -639,8 +620,8 @@ void TouchControls::step(float dtime)
} }
// simulate keyboard repeats // simulate keyboard repeats
buttons_step(m_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); buttonsStep(m_buttons, dtime);
buttons_step(m_overflow_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); buttonsStep(m_overflow_buttons, dtime);
// joystick // joystick
applyJoystickStatus(); applyJoystickStatus();

View file

@ -67,9 +67,6 @@ struct button_info
SECOND_TEXTURE SECOND_TEXTURE
} toggleable = NOT_TOGGLEABLE; } toggleable = NOT_TOGGLEABLE;
std::string toggle_textures[2]; std::string toggle_textures[2];
void emitAction(bool action, video::IVideoDriver *driver,
IEventReceiver *receiver, ISimpleTextureSource *tsrc);
}; };
@ -186,6 +183,19 @@ private:
std::shared_ptr<IGUIStaticText> m_status_text; std::shared_ptr<IGUIStaticText> m_status_text;
// Note: TouchControls intentionally uses IGUIImage instead of IGUIButton
// for its buttons. We only want static image display, not interactivity,
// from Irrlicht.
void emitKeyboardEvent(EKEY_CODE keycode, bool pressed);
void loadButtonTexture(IGUIImage *gui_button, const std::string &path);
void buttonEmitAction(button_info &btn, bool action);
bool buttonsHandlePress(std::vector<button_info> &buttons, size_t pointer_id, IGUIElement *element);
bool buttonsHandleRelease(std::vector<button_info> &buttons, size_t pointer_id);
bool buttonsStep(std::vector<button_info> &buttons, float dtime);
void toggleOverflowMenu(); void toggleOverflowMenu();
void updateVisibility(); void updateVisibility();
void releaseAll(); void releaseAll();

View file

@ -1,6 +1,7 @@
/* /*
CGUITTFont FreeType class for Irrlicht CGUITTFont FreeType class for Irrlicht
Copyright (c) 2009-2010 John Norman Copyright (c) 2009-2010 John Norman
with changes from Luanti contributors:
Copyright (c) 2016 Nathanaëlle Courant Copyright (c) 2016 Nathanaëlle Courant
Copyright (c) 2023 Caleb Butler Copyright (c) 2023 Caleb Butler
@ -31,14 +32,13 @@
*/ */
#include <iostream> #include <iostream>
#include "log.h"
#include "filesys.h"
#include "debug.h"
#include "CGUITTFont.h" #include "CGUITTFont.h"
#include "CMeshBuffer.h"
#include "IFileSystem.h" #include "IFileSystem.h"
#include "IGUIEnvironment.h" #include "IGUIEnvironment.h"
#include "IMeshManipulator.h"
#include "IMeshSceneNode.h"
#include "ISceneManager.h"
#include "ISceneNode.h"
namespace irr namespace irr
{ {
@ -46,9 +46,9 @@ namespace gui
{ {
// Manages the FT_Face cache. // Manages the FT_Face cache.
struct SGUITTFace : public virtual irr::IReferenceCounted struct SGUITTFace : public irr::IReferenceCounted
{ {
SGUITTFace() : face_buffer(0), face_buffer_size(0) SGUITTFace()
{ {
memset((void*)&face, 0, sizeof(FT_Face)); memset((void*)&face, 0, sizeof(FT_Face));
} }
@ -56,46 +56,29 @@ struct SGUITTFace : public virtual irr::IReferenceCounted
~SGUITTFace() ~SGUITTFace()
{ {
FT_Done_Face(face); FT_Done_Face(face);
delete[] face_buffer;
} }
FT_Face face; FT_Face face;
FT_Byte* face_buffer; std::string face_buffer;
FT_Long face_buffer_size;
}; };
// Static variables. // Static variables.
FT_Library CGUITTFont::c_library; FT_Library CGUITTFont::c_library;
std::map<io::path, SGUITTFace*> CGUITTFont::c_faces; std::map<io::path, SGUITTFace*> CGUITTFont::c_faces;
bool CGUITTFont::c_libraryLoaded = false; bool CGUITTFont::c_libraryLoaded = false;
scene::IMesh* CGUITTFont::shared_plane_ptr_ = 0;
scene::SMesh CGUITTFont::shared_plane_;
// //
/** Checks that no dimension of the FT_BitMap object is negative. If either is
* negative, abort execution.
*/
inline void checkFontBitmapSize(const FT_Bitmap &bits)
{
if ((s32)bits.rows < 0 || (s32)bits.width < 0) {
std::cout << "Insane font glyph size. File: "
<< __FILE__ << " Line " << __LINE__
<< std::endl;
abort();
}
}
video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const
{ {
// Make sure our casts to s32 in the loops below will not cause problems // Make sure our casts to s32 in the loops below will not cause problems
checkFontBitmapSize(bits); if ((s32)bits.rows < 0 || (s32)bits.width < 0)
FATAL_ERROR("Insane font glyph size");
// Determine what our texture size should be. // Determine what our texture size should be.
// Add 1 because textures are inclusive-exclusive. // Add 1 because textures are inclusive-exclusive.
core::dimension2du d(bits.width + 1, bits.rows + 1); core::dimension2du d(bits.width + 1, bits.rows + 1);
core::dimension2du texture_size; core::dimension2du texture_size;
//core::dimension2du texture_size(bits.width + 1, bits.rows + 1);
// Create and load our image now. // Create and load our image now.
video::IImage* image = 0; video::IImage* image = 0;
@ -147,36 +130,36 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide
for (s32 x = 0; x < (s32)bits.width; ++x) for (s32 x = 0; x < (s32)bits.width; ++x)
{ {
image_data[y * image_pitch + x] |= static_cast<u32>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24; image_data[y * image_pitch + x] |= static_cast<u32>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24;
//data[y * image_pitch + x] |= ((u32)(*bitsdata++) << 24);
} }
glyph_data += bits.pitch; glyph_data += bits.pitch;
} }
break; break;
} }
default: default:
// TODO: error message? errorstream << "CGUITTFont: unknown pixel mode " << (int)bits.pixel_mode << std::endl;
return 0; return 0;
} }
return image; return image;
} }
void SGUITTGlyph::preload(u32 char_index, FT_Face face, video::IVideoDriver* driver, u32 font_size, const FT_Int32 loadFlags) void SGUITTGlyph::preload(u32 char_index, FT_Face face, CGUITTFont *parent, u32 font_size, const FT_Int32 loadFlags)
{ {
if (isLoaded) return;
// Set the size of the glyph. // Set the size of the glyph.
FT_Set_Pixel_Sizes(face, 0, font_size); FT_Set_Pixel_Sizes(face, 0, font_size);
// Attempt to load the glyph. // Attempt to load the glyph.
if (FT_Load_Glyph(face, char_index, loadFlags) != FT_Err_Ok) auto err = FT_Load_Glyph(face, char_index, loadFlags);
// TODO: error message? if (err != FT_Err_Ok) {
warningstream << "SGUITTGlyph: failed to load glyph " << char_index
<< " with error: " << (int)err << std::endl;
return; return;
}
FT_GlyphSlot glyph = face->glyph; FT_GlyphSlot glyph = face->glyph;
FT_Bitmap bits = glyph->bitmap; const FT_Bitmap &bits = glyph->bitmap;
// Setup the glyph information here: // Setup the glyph information here:
advance = glyph->advance; advance = core::vector2di(glyph->advance.x, glyph->advance.y);
offset = core::vector2di(glyph->bitmap_left, glyph->bitmap_top); offset = core::vector2di(glyph->bitmap_left, glyph->bitmap_top);
// Try to get the last page with available slots. // Try to get the last page with available slots.
@ -187,7 +170,6 @@ void SGUITTGlyph::preload(u32 char_index, FT_Face face, video::IVideoDriver* dri
{ {
page = parent->createGlyphPage(bits.pixel_mode); page = parent->createGlyphPage(bits.pixel_mode);
if (!page) if (!page)
// TODO: add error message?
return; return;
} }
@ -205,10 +187,7 @@ void SGUITTGlyph::preload(u32 char_index, FT_Face face, video::IVideoDriver* dri
--page->available_slots; --page->available_slots;
// We grab the glyph bitmap here so the data won't be removed when the next glyph is loaded. // We grab the glyph bitmap here so the data won't be removed when the next glyph is loaded.
surface = createGlyphImage(bits, driver); surface = createGlyphImage(bits, parent->getDriver());
// Set our glyph as loaded.
isLoaded = true;
} }
void SGUITTGlyph::unload() void SGUITTGlyph::unload()
@ -218,7 +197,8 @@ void SGUITTGlyph::unload()
surface->drop(); surface->drop();
surface = 0; surface = 0;
} }
isLoaded = false; // reset isLoaded to false
source_rect = core::recti();
} }
////////////////////// //////////////////////
@ -251,14 +231,13 @@ CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filen
//! Constructor. //! Constructor.
CGUITTFont::CGUITTFont(IGUIEnvironment *env) CGUITTFont::CGUITTFont(IGUIEnvironment *env)
: use_monochrome(false), use_transparency(true), use_hinting(true), use_auto_hinting(true), : use_monochrome(false), use_transparency(true), use_hinting(true), use_auto_hinting(true),
batch_load_size(1), Device(0), Environment(env), Driver(0), GlobalKerningWidth(0), GlobalKerningHeight(0), batch_load_size(1), Driver(0), GlobalKerningWidth(0), GlobalKerningHeight(0),
shadow_offset(0), shadow_alpha(0), fallback(0) shadow_offset(0), shadow_alpha(0), fallback(0)
{ {
if (Environment) if (env) {
{
// don't grab environment, to avoid circular references // don't grab environment, to avoid circular references
Driver = Environment->getVideoDriver(); Driver = env->getVideoDriver();
} }
if (Driver) if (Driver)
@ -270,13 +249,10 @@ shadow_offset(0), shadow_alpha(0), fallback(0)
bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency) bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency)
{ {
// Some sanity checks. // Some sanity checks.
if (Environment == 0 || Driver == 0) return false; if (!Driver) return false;
if (size == 0) return false; if (size == 0) return false;
if (filename.size() == 0) return false; if (filename.empty()) return false;
io::IFileSystem* filesystem = Environment->getFileSystem();
irr::ILogger* logger = (Device != 0 ? Device->getLogger() : 0);
// FIXME: this is always null ^
this->size = size; this->size = size;
this->filename = filename; this->filename = filename;
@ -285,62 +261,33 @@ bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antia
this->use_transparency = transparency; this->use_transparency = transparency;
update_load_flags(); update_load_flags();
// Log. infostream << "CGUITTFont: Creating new font: " << filename.c_str() << " "
if (logger) << size << "pt " << (antialias ? "+antialias " : "-antialias ")
logger->log("CGUITTFont", (core::stringc(L"Creating new font: ") + filename + " " + core::stringc(size) + "pt " + (antialias ? "+antialias " : "-antialias ") + (transparency ? "+transparency" : "-transparency")).c_str(), irr::ELL_INFORMATION); << (transparency ? "+transparency" : "-transparency") << std::endl;
// Grab the face. // Grab the face.
SGUITTFace* face = 0; SGUITTFace* face = nullptr;
auto node = c_faces.find(filename); auto node = c_faces.find(filename);
if (node == c_faces.end()) if (node == c_faces.end()) {
{
face = new SGUITTFace(); face = new SGUITTFace();
if (!fs::ReadFile(filename.c_str(), face->face_buffer, true)) {
delete face;
return false;
}
// Create the face.
if (FT_New_Memory_Face(c_library,
reinterpret_cast<const FT_Byte*>(face->face_buffer.data()),
face->face_buffer.size(), 0, &face->face))
{
errorstream << "CGUITTFont: FT_New_Memory_Face failed." << std::endl;
delete face;
return false;
}
c_faces.emplace(filename, face); c_faces.emplace(filename, face);
} else {
if (filesystem)
{
// Read in the file data.
io::IReadFile* file = filesystem->createAndOpenFile(filename);
if (file == 0)
{
if (logger) logger->log("CGUITTFont", "Failed to open the file.", irr::ELL_INFORMATION);
c_faces.erase(filename);
delete face;
face = 0;
return false;
}
face->face_buffer = new FT_Byte[file->getSize()];
file->read(face->face_buffer, file->getSize());
face->face_buffer_size = file->getSize();
file->drop();
// Create the face.
if (FT_New_Memory_Face(c_library, face->face_buffer, face->face_buffer_size, 0, &face->face))
{
if (logger) logger->log("CGUITTFont", "FT_New_Memory_Face failed.", irr::ELL_INFORMATION);
c_faces.erase(filename);
delete face;
face = 0;
return false;
}
}
else
{
if (FT_New_Face(c_library, reinterpret_cast<const char*>(filename.c_str()), 0, &face->face))
{
if (logger) logger->log("CGUITTFont", "FT_New_Face failed.", irr::ELL_INFORMATION);
c_faces.erase(filename);
delete face;
face = 0;
return false;
}
}
}
else
{
// Using another instance of this face. // Using another instance of this face.
face = node->second; face = node->second;
face->grab(); face->grab();
@ -353,20 +300,12 @@ bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antia
FT_Set_Pixel_Sizes(tt_face, size, 0); FT_Set_Pixel_Sizes(tt_face, size, 0);
font_metrics = tt_face->size->metrics; font_metrics = tt_face->size->metrics;
verbosestream << tt_face->num_glyphs << " glyphs, ascender=" << font_metrics.ascender
<< " height=" << font_metrics.height << std::endl;
// Allocate our glyphs. // Allocate our glyphs.
Glyphs.clear(); Glyphs.clear();
Glyphs.reallocate(tt_face->num_glyphs);
Glyphs.set_used(tt_face->num_glyphs); Glyphs.set_used(tt_face->num_glyphs);
for (FT_Long i = 0; i < tt_face->num_glyphs; ++i)
{
Glyphs[i].isLoaded = false;
Glyphs[i].glyph_page = 0;
Glyphs[i].source_rect = core::recti();
Glyphs[i].offset = core::vector2di();
Glyphs[i].advance = FT_Vector();
Glyphs[i].surface = 0;
Glyphs[i].parent = this;
}
// Cache the first 127 ascii characters. // Cache the first 127 ascii characters.
u32 old_size = batch_load_size; u32 old_size = batch_load_size;
@ -444,7 +383,7 @@ CGUITTGlyphPage* CGUITTFont::getLastGlyphPage() const
return page; return page;
} }
CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8& pixel_mode) CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8 pixel_mode)
{ {
CGUITTGlyphPage* page = 0; CGUITTGlyphPage* page = 0;
@ -481,7 +420,8 @@ CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8& pixel_mode)
page_texture_size = max_texture_size; page_texture_size = max_texture_size;
if (!page->createPageTexture(pixel_mode, page_texture_size)) { if (!page->createPageTexture(pixel_mode, page_texture_size)) {
// TODO: add error message? errorstream << "CGUITTGlyphPage: failed to create texture ("
<< page_texture_size.Width << "x" << page_texture_size.Height << ")" << std::endl;
delete page; delete page;
return 0; return 0;
} }
@ -622,13 +562,12 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
else if (fallback != 0) else if (fallback != 0)
{ {
// Let the fallback font draw it, this isn't super efficient but hopefully that doesn't matter // Let the fallback font draw it, this isn't super efficient but hopefully that doesn't matter
wchar_t l1[] = { (wchar_t) currentChar, 0 }, l2 = (wchar_t) previousChar; wchar_t l1[] = { (wchar_t) currentChar, 0 };
if (visible) if (visible)
{ {
// Apply kerning. // Apply kerning.
offset.X += fallback->getKerningWidth(l1, &l2); offset += fallback->getKerning(*l1, (wchar_t) previousChar);
offset.Y += fallback->getKerningHeight();
const u32 current_color = iter - utext.begin(); const u32 current_color = iter - utext.begin();
fallback->draw(core::stringw(l1), fallback->draw(core::stringw(l1),
@ -688,11 +627,6 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
} }
} }
core::dimension2d<u32> CGUITTFont::getCharDimension(const wchar_t ch) const
{
return core::dimension2d<u32>(getWidthFromCharacter(ch), getHeightFromCharacter(ch));
}
core::dimension2d<u32> CGUITTFont::getDimension(const wchar_t* text) const core::dimension2d<u32> CGUITTFont::getDimension(const wchar_t* text) const
{ {
return getDimension(convertWCharToU32String(text)); return getDimension(convertWCharToU32String(text));
@ -759,21 +693,12 @@ core::dimension2d<u32> CGUITTFont::getDimension(const std::u32string& text) cons
return text_dimension; return text_dimension;
} }
inline u32 CGUITTFont::getWidthFromCharacter(wchar_t c) const
{
return getWidthFromCharacter((char32_t)c);
}
inline u32 CGUITTFont::getWidthFromCharacter(char32_t c) const inline u32 CGUITTFont::getWidthFromCharacter(char32_t c) const
{ {
// Set the size of the face.
// This is because we cache faces and the face may have been set to a different size.
//FT_Set_Pixel_Sizes(tt_face, 0, size);
u32 n = getGlyphIndexByChar(c); u32 n = getGlyphIndexByChar(c);
if (n > 0) if (n > 0)
{ {
int w = Glyphs[n-1].advance.x / 64; int w = Glyphs[n-1].advance.X / 64;
return w; return w;
} }
if (fallback != 0) if (fallback != 0)
@ -787,17 +712,8 @@ inline u32 CGUITTFont::getWidthFromCharacter(char32_t c) const
else return (font_metrics.ascender / 64) / 2; else return (font_metrics.ascender / 64) / 2;
} }
inline u32 CGUITTFont::getHeightFromCharacter(wchar_t c) const
{
return getHeightFromCharacter((char32_t)c);
}
inline u32 CGUITTFont::getHeightFromCharacter(char32_t c) const inline u32 CGUITTFont::getHeightFromCharacter(char32_t c) const
{ {
// Set the size of the face.
// This is because we cache faces and the face may have been set to a different size.
//FT_Set_Pixel_Sizes(tt_face, 0, size);
u32 n = getGlyphIndexByChar(c); u32 n = getGlyphIndexByChar(c);
if (n > 0) if (n > 0)
{ {
@ -816,11 +732,6 @@ inline u32 CGUITTFont::getHeightFromCharacter(char32_t c) const
else return (font_metrics.ascender / 64) / 2; else return (font_metrics.ascender / 64) / 2;
} }
u32 CGUITTFont::getGlyphIndexByChar(wchar_t c) const
{
return getGlyphIndexByChar((char32_t)c);
}
u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const
{ {
// Get the glyph. // Get the glyph.
@ -831,7 +742,7 @@ u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const
return 0; return 0;
// If our glyph is already loaded, don't bother doing any batch loading code. // If our glyph is already loaded, don't bother doing any batch loading code.
if (glyph != 0 && Glyphs[glyph - 1].isLoaded) if (glyph != 0 && Glyphs[glyph - 1].isLoaded())
return glyph; return glyph;
// Determine our batch loading positions. // Determine our batch loading positions.
@ -850,9 +761,10 @@ u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const
if (char_index) if (char_index)
{ {
SGUITTGlyph& glyph = Glyphs[char_index - 1]; SGUITTGlyph& glyph = Glyphs[char_index - 1];
if (!glyph.isLoaded) if (!glyph.isLoaded())
{ {
glyph.preload(char_index, tt_face, Driver, size, load_flags); auto *this2 = const_cast<CGUITTFont*>(this); // oh well
glyph.preload(char_index, tt_face, this2, size, load_flags);
Glyph_Pages[glyph.glyph_page]->pushGlyphToBePaged(&glyph); Glyph_Pages[glyph.glyph_page]->pushGlyphToBePaged(&glyph);
} }
} }
@ -871,11 +783,10 @@ s32 CGUITTFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
s32 CGUITTFont::getCharacterFromPos(const std::u32string& text, s32 pixel_x) const s32 CGUITTFont::getCharacterFromPos(const std::u32string& text, s32 pixel_x) const
{ {
s32 x = 0; s32 x = 0;
//s32 idx = 0;
u32 character = 0; u32 character = 0;
char32_t previousChar = 0; char32_t previousChar = 0;
std::u32string::const_iterator iter = text.begin(); auto iter = text.begin();
while (iter != text.end()) while (iter != text.end())
{ {
char32_t c = *iter; char32_t c = *iter;
@ -906,28 +817,6 @@ void CGUITTFont::setKerningHeight(s32 kerning)
GlobalKerningHeight = kerning; GlobalKerningHeight = kerning;
} }
s32 CGUITTFont::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previousLetter) const
{
if (tt_face == 0)
return GlobalKerningWidth;
if (thisLetter == 0 || previousLetter == 0)
return 0;
return getKerningWidth((char32_t)*thisLetter, (char32_t)*previousLetter);
}
s32 CGUITTFont::getKerningWidth(const char32_t thisLetter, const char32_t previousLetter) const
{
// Return only the kerning width.
return getKerning(thisLetter, previousLetter).X;
}
s32 CGUITTFont::getKerningHeight() const
{
// FreeType 2 currently doesn't return any height kerning information.
return GlobalKerningHeight;
}
core::vector2di CGUITTFont::getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const core::vector2di CGUITTFont::getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const
{ {
return getKerning((char32_t)thisLetter, (char32_t)previousLetter); return getKerning((char32_t)thisLetter, (char32_t)previousLetter);
@ -949,11 +838,8 @@ core::vector2di CGUITTFont::getKerning(const char32_t thisLetter, const char32_t
// If we don't have this glyph, ask fallback font // If we don't have this glyph, ask fallback font
if (n == 0) if (n == 0)
{ {
if (fallback != 0) { if (fallback)
wchar_t l1 = (wchar_t) thisLetter, l2 = (wchar_t) previousLetter; ret = fallback->getKerning((wchar_t) thisLetter, (wchar_t) previousLetter);
ret.X = fallback->getKerningWidth(&l1, &l2);
ret.Y = fallback->getKerningHeight();
}
return ret; return ret;
} }
@ -1029,196 +915,6 @@ video::ITexture* CGUITTFont::getPageTextureByIndex(const u32& page_index) const
return 0; return 0;
} }
void CGUITTFont::createSharedPlane()
{
/*
2___3
| /|
| / | <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
|/ | <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
0---1
*/
using namespace core;
using namespace video;
using namespace scene;
S3DVertex vertices[4];
u16 indices[6] = {0,2,3,3,1,0};
vertices[0] = S3DVertex(vector3df(0,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,1));
vertices[1] = S3DVertex(vector3df(1,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,1));
vertices[2] = S3DVertex(vector3df(0, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,0));
vertices[3] = S3DVertex(vector3df(1, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,0));
SMeshBuffer* buf = new SMeshBuffer();
buf->append(vertices, 4, indices, 6);
shared_plane_.addMeshBuffer( buf );
shared_plane_.setHardwareMappingHint(EHM_STATIC);
shared_plane_ptr_ = &shared_plane_;
buf->drop(); //the addMeshBuffer method will grab it, so we can drop this ptr.
}
core::dimension2d<u32> CGUITTFont::getDimensionUntilEndOfLine(const wchar_t* p) const
{
core::stringw s;
for (const wchar_t* temp = p; temp && *temp != '\0' && *temp != L'\r' && *temp != L'\n'; ++temp )
s.append(*temp);
return getDimension(s.c_str());
}
core::array<scene::ISceneNode*> CGUITTFont::addTextSceneNode(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent, const video::SColor& color, bool center)
{
using namespace core;
using namespace video;
using namespace scene;
array<scene::ISceneNode*> container;
if (!Driver || !smgr) return container;
if (!parent)
parent = smgr->addEmptySceneNode(smgr->getRootSceneNode(), -1);
// if you don't specify parent, then we add an empty node attached to the root node
// this is generally undesirable.
if (!shared_plane_ptr_) //this points to a static mesh that contains the plane
createSharedPlane(); //if it's not initialized, we create one.
dimension2d<s32> text_size(getDimension(text)); //convert from unsigned to signed.
vector3df start_point(0, 0, 0), offset;
/** NOTICE:
Because we are considering adding texts into 3D world, all Y axis vectors are inverted.
**/
// There's currently no "vertical center" concept when you apply text scene node to the 3D world.
if (center)
{
offset.X = start_point.X = -text_size.Width / 2.f;
offset.Y = start_point.Y = +text_size.Height/ 2.f;
offset.X += (text_size.Width - getDimensionUntilEndOfLine(text).Width) >> 1;
}
// the default font material
SMaterial mat;
mat.ZWriteEnable = video::EZW_OFF;
mat.MaterialType = use_transparency ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
mat.MaterialTypeParam = 0.01f;
wchar_t current_char = 0, previous_char = 0;
u32 n = 0;
array<u32> glyph_indices;
while (*text)
{
current_char = *text;
bool line_break=false;
if (current_char == L'\r') // Mac or Windows breaks
{
line_break = true;
if (*(text + 1) == L'\n') // Windows line breaks.
current_char = *(++text);
}
else if (current_char == L'\n') // Unix breaks
{
line_break = true;
}
if (line_break)
{
previous_char = 0;
offset.Y -= tt_face->size->metrics.ascender / 64;
offset.X = start_point.X;
if (center)
offset.X += (text_size.Width - getDimensionUntilEndOfLine(text+1).Width) >> 1;
++text;
}
else
{
n = getGlyphIndexByChar(current_char);
if (n > 0)
{
glyph_indices.push_back( n );
// Store glyph size and offset informations.
SGUITTGlyph const& glyph = Glyphs[n-1];
u32 texw = glyph.source_rect.getWidth();
u32 texh = glyph.source_rect.getHeight();
s32 offx = glyph.offset.X;
s32 offy = (font_metrics.ascender / 64) - glyph.offset.Y;
// Apply kerning.
vector2di k = getKerning(current_char, previous_char);
offset.X += k.X;
offset.Y += k.Y;
vector3df current_pos(offset.X + offx, offset.Y - offy, 0);
dimension2d<u32> letter_size = dimension2d<u32>(texw, texh);
// Now we copy planes corresponding to the letter size.
IMeshManipulator* mani = smgr->getMeshManipulator();
IMesh* meshcopy = mani->createMeshCopy(shared_plane_ptr_);
mani->scale(meshcopy, vector3df((f32)letter_size.Width, (f32)letter_size.Height, 1));
ISceneNode* current_node = smgr->addMeshSceneNode(meshcopy, parent, -1, current_pos);
meshcopy->drop();
current_node->getMaterial(0) = mat;
current_node->setAutomaticCulling(EAC_OFF);
current_node->setIsDebugObject(true); //so the picking won't have any effect on individual letter
//current_node->setDebugDataVisible(EDS_BBOX); //de-comment this when debugging
container.push_back(current_node);
}
offset.X += getWidthFromCharacter(current_char);
// Note that fallback font handling is missing here (Minetest never uses this)
previous_char = current_char;
++text;
}
}
update_glyph_pages();
//only after we update the textures can we use the glyph page textures.
for (u32 i = 0; i < glyph_indices.size(); ++i)
{
u32 n = glyph_indices[i];
SGUITTGlyph const& glyph = Glyphs[n-1];
ITexture* current_tex = Glyph_Pages[glyph.glyph_page]->texture;
f32 page_texture_size = (f32)current_tex->getSize().Width;
//Now we calculate the UV position according to the texture size and the source rect.
//
// 2___3
// | /|
// | / | <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
// |/ | <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
// 0---1
//
f32 u1 = glyph.source_rect.UpperLeftCorner.X / page_texture_size;
f32 u2 = u1 + (glyph.source_rect.getWidth() / page_texture_size);
f32 v1 = glyph.source_rect.UpperLeftCorner.Y / page_texture_size;
f32 v2 = v1 + (glyph.source_rect.getHeight() / page_texture_size);
//we can be quite sure that this is IMeshSceneNode, because we just added them in the above loop.
IMeshSceneNode* node = static_cast<IMeshSceneNode*>(container[i]);
S3DVertex* pv = static_cast<S3DVertex*>(node->getMesh()->getMeshBuffer(0)->getVertices());
//pv[0].TCoords.Y = pv[1].TCoords.Y = (letter_size.Height - 1) / static_cast<f32>(letter_size.Height);
//pv[1].TCoords.X = pv[3].TCoords.X = (letter_size.Width - 1) / static_cast<f32>(letter_size.Width);
pv[0].TCoords = vector2df(u1, v2);
pv[1].TCoords = vector2df(u2, v2);
pv[2].TCoords = vector2df(u1, v1);
pv[3].TCoords = vector2df(u2, v1);
container[i]->getMaterial(0).setTexture(0, current_tex);
}
return container;
}
std::u32string CGUITTFont::convertWCharToU32String(const wchar_t* const charArray) const std::u32string CGUITTFont::convertWCharToU32String(const wchar_t* const charArray) const
{ {
static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "unexpected wchar size"); static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "unexpected wchar size");

View file

@ -32,17 +32,17 @@
#pragma once #pragma once
#include <ft2build.h>
#include <map> #include <map>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "IGUIEnvironment.h" #include "IGUIEnvironment.h"
#include "IGUIFont.h" #include "IGUIFont.h"
#include "ISceneManager.h"
#include "IVideoDriver.h" #include "IVideoDriver.h"
#include "IrrlichtDevice.h" #include "IrrlichtDevice.h"
#include "SMesh.h" #include "SMesh.h"
#include "util/enriched_string.h" #include "util/enriched_string.h"
#include "util/basic_macros.h" #include "util/basic_macros.h"
#include FT_FREETYPE_H
namespace irr namespace irr
{ {
@ -56,26 +56,22 @@ namespace gui
{ {
//! Constructor. //! Constructor.
SGUITTGlyph() : SGUITTGlyph() :
isLoaded(false),
glyph_page(0), glyph_page(0),
source_rect(), source_rect(),
offset(), offset(),
advance(), advance(),
surface(0), surface(0)
parent(0)
{} {}
DISABLE_CLASS_COPY(SGUITTGlyph); DISABLE_CLASS_COPY(SGUITTGlyph);
//! This class would be trivially copyable except for the reference count on `surface`. //! This class would be trivially copyable except for the reference count on `surface`.
SGUITTGlyph(SGUITTGlyph &&other) noexcept : SGUITTGlyph(SGUITTGlyph &&other) noexcept :
isLoaded(other.isLoaded),
glyph_page(other.glyph_page), glyph_page(other.glyph_page),
source_rect(other.source_rect), source_rect(other.source_rect),
offset(other.offset), offset(other.offset),
advance(other.advance), advance(other.advance),
surface(other.surface), surface(other.surface)
parent(other.parent)
{ {
other.surface = 0; other.surface = 0;
} }
@ -83,12 +79,17 @@ namespace gui
//! Destructor. //! Destructor.
~SGUITTGlyph() { unload(); } ~SGUITTGlyph() { unload(); }
//! If true, the glyph has been loaded.
inline bool isLoaded() const {
return source_rect != core::recti();
}
//! Preload the glyph. //! Preload the glyph.
//! The preload process occurs when the program tries to cache the glyph from FT_Library. //! The preload process occurs when the program tries to cache the glyph from FT_Library.
//! However, it simply defines the SGUITTGlyph's properties and will only create the page //! However, it simply defines the SGUITTGlyph's properties and will only create the page
//! textures if necessary. The actual creation of the textures should only occur right //! textures if necessary. The actual creation of the textures should only occur right
//! before the batch draw call. //! before the batch draw call.
void preload(u32 char_index, FT_Face face, video::IVideoDriver* driver, u32 font_size, const FT_Int32 loadFlags); void preload(u32 char_index, FT_Face face, CGUITTFont *parent, u32 font_size, const FT_Int32 loadFlags);
//! Unloads the glyph. //! Unloads the glyph.
void unload(); void unload();
@ -96,9 +97,6 @@ namespace gui
//! Creates the IImage object from the FT_Bitmap. //! Creates the IImage object from the FT_Bitmap.
video::IImage* createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const; video::IImage* createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const;
//! If true, the glyph has been loaded.
bool isLoaded;
//! The page the glyph is on. //! The page the glyph is on.
u32 glyph_page; u32 glyph_page;
@ -109,14 +107,11 @@ namespace gui
core::vector2di offset; core::vector2di offset;
//! Glyph advance information. //! Glyph advance information.
FT_Vector advance; core::vector2di advance;
//! This is just the temporary image holder. After this glyph is paged, //! This is just the temporary image holder. After this glyph is paged,
//! it will be dropped. //! it will be dropped.
mutable video::IImage* surface; mutable video::IImage* surface;
//! The pointer pointing to the parent (CGUITTFont)
CGUITTFont* parent;
}; };
//! Holds a sheet of glyphs. //! Holds a sheet of glyphs.
@ -130,7 +125,8 @@ namespace gui
{ {
if (driver) if (driver)
driver->removeTexture(texture); driver->removeTexture(texture);
else texture->drop(); else
texture->drop();
} }
} }
@ -184,19 +180,11 @@ namespace gui
for (u32 i = 0; i < glyph_to_be_paged.size(); ++i) for (u32 i = 0; i < glyph_to_be_paged.size(); ++i)
{ {
const SGUITTGlyph* glyph = glyph_to_be_paged[i]; const SGUITTGlyph* glyph = glyph_to_be_paged[i];
if (glyph && glyph->isLoaded) if (glyph && glyph->surface)
{ {
if (glyph->surface) glyph->surface->copyTo(pageholder, glyph->source_rect.UpperLeftCorner);
{ glyph->surface->drop();
glyph->surface->copyTo(pageholder, glyph->source_rect.UpperLeftCorner); glyph->surface = 0;
glyph->surface->drop();
glyph->surface = 0;
}
else
{
; // TODO: add error message?
//currently, if we failed to create the image, just ignore this operation.
}
} }
} }
@ -238,77 +226,70 @@ namespace gui
virtual ~CGUITTFont(); virtual ~CGUITTFont();
//! Sets the amount of glyphs to batch load. //! Sets the amount of glyphs to batch load.
virtual void setBatchLoadSize(u32 batch_size) { batch_load_size = batch_size; } void setBatchLoadSize(u32 batch_size) { batch_load_size = batch_size; }
//! Sets the maximum texture size for a page of glyphs. //! Sets the maximum texture size for a page of glyphs.
virtual void setMaxPageTextureSize(const core::dimension2du& texture_size) { max_page_texture_size = texture_size; } void setMaxPageTextureSize(const core::dimension2du& texture_size) { max_page_texture_size = texture_size; }
//! Get the font size. //! Get the font size.
virtual u32 getFontSize() const { return size; } u32 getFontSize() const { return size; }
//! Check the font's transparency. //! Check the font's transparency.
virtual bool isTransparent() const { return use_transparency; } bool isTransparent() const { return use_transparency; }
//! Check if the font auto-hinting is enabled. //! Check if the font auto-hinting is enabled.
//! Auto-hinting is FreeType's built-in font hinting engine. //! Auto-hinting is FreeType's built-in font hinting engine.
virtual bool useAutoHinting() const { return use_auto_hinting; } bool useAutoHinting() const { return use_auto_hinting; }
//! Check if the font hinting is enabled. //! Check if the font hinting is enabled.
virtual bool useHinting() const { return use_hinting; } bool useHinting() const { return use_hinting; }
//! Check if the font is being loaded as a monochrome font. //! Check if the font is being loaded as a monochrome font.
//! The font can either be a 256 color grayscale font, or a 2 color monochrome font. //! The font can either be a 256 color grayscale font, or a 2 color monochrome font.
virtual bool useMonochrome() const { return use_monochrome; } bool useMonochrome() const { return use_monochrome; }
//! Tells the font to allow transparency when rendering. //! Tells the font to allow transparency when rendering.
//! Default: true. //! Default: true.
//! \param flag If true, the font draws using transparency. //! \param flag If true, the font draws using transparency.
virtual void setTransparency(const bool flag); void setTransparency(const bool flag);
//! Tells the font to use monochrome rendering. //! Tells the font to use monochrome rendering.
//! Default: false. //! Default: false.
//! \param flag If true, the font draws using a monochrome image. If false, the font uses a grayscale image. //! \param flag If true, the font draws using a monochrome image. If false, the font uses a grayscale image.
virtual void setMonochrome(const bool flag); void setMonochrome(const bool flag);
//! Enables or disables font hinting. //! Enables or disables font hinting.
//! Default: Hinting and auto-hinting true. //! Default: Hinting and auto-hinting true.
//! \param enable If false, font hinting is turned off. If true, font hinting is turned on. //! \param enable If false, font hinting is turned off. If true, font hinting is turned on.
//! \param enable_auto_hinting If true, FreeType uses its own auto-hinting algorithm. If false, it tries to use the algorithm specified by the font. //! \param enable_auto_hinting If true, FreeType uses its own auto-hinting algorithm. If false, it tries to use the algorithm specified by the font.
virtual void setFontHinting(const bool enable, const bool enable_auto_hinting = true); void setFontHinting(const bool enable, const bool enable_auto_hinting = true);
//! Draws some text and clips it to the specified rectangle if wanted. //! Draws some text and clips it to the specified rectangle if wanted.
virtual void draw(const core::stringw& text, const core::rect<s32>& position, virtual void draw(const core::stringw& text, const core::rect<s32>& position,
video::SColor color, bool hcenter=false, bool vcenter=false, video::SColor color, bool hcenter=false, bool vcenter=false,
const core::rect<s32>* clip=0); const core::rect<s32>* clip=0) override;
void draw(const EnrichedString& text, const core::rect<s32>& position, void draw(const EnrichedString& text, const core::rect<s32>& position,
bool hcenter=false, bool vcenter=false, bool hcenter=false, bool vcenter=false,
const core::rect<s32>* clip=0); const core::rect<s32>* clip=0);
//! Returns the dimension of a character produced by this font.
virtual core::dimension2d<u32> getCharDimension(const wchar_t ch) const;
//! Returns the dimension of a text string. //! Returns the dimension of a text string.
virtual core::dimension2d<u32> getDimension(const wchar_t* text) const; virtual core::dimension2du getDimension(const wchar_t* text) const override;
//! Calculates the index of the character in the text which is on a specific position. //! Calculates the index of the character in the text which is on a specific position.
virtual s32 getCharacterFromPos(const wchar_t* text, s32 pixel_x) const; virtual s32 getCharacterFromPos(const wchar_t* text, s32 pixel_x) const override;
//! Sets global kerning width for the font. //! Sets global kerning width for the font.
virtual void setKerningWidth(s32 kerning); virtual void setKerningWidth(s32 kerning) override;
//! Sets global kerning height for the font. //! Sets global kerning height for the font.
virtual void setKerningHeight(s32 kerning); virtual void setKerningHeight(s32 kerning) override;
//! Gets kerning values (distance between letters) for the font. If no parameters are provided,
virtual s32 getKerningWidth(const wchar_t* thisLetter=0, const wchar_t* previousLetter=0) const;
virtual s32 getKerningWidth(const char32_t thisLetter=0, const char32_t previousLetter=0) const;
//! Returns the distance between letters //! Returns the distance between letters
virtual s32 getKerningHeight() const; virtual core::vector2di getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const override;
//! Define which characters should not be drawn by the font. //! Define which characters should not be drawn by the font.
virtual void setInvisibleCharacters(const wchar_t *s); virtual void setInvisibleCharacters(const wchar_t *s) override;
//! Get the last glyph page if there's still available slots. //! Get the last glyph page if there's still available slots.
//! If not, it will return zero. //! If not, it will return zero.
@ -317,7 +298,7 @@ namespace gui
//! Create a new glyph page texture. //! Create a new glyph page texture.
//! \param pixel_mode the pixel mode defined by FT_Pixel_Mode //! \param pixel_mode the pixel mode defined by FT_Pixel_Mode
//should be better typed. fix later. //should be better typed. fix later.
CGUITTGlyphPage* createGlyphPage(const u8& pixel_mode); CGUITTGlyphPage* createGlyphPage(const u8 pixel_mode);
//! Get the last glyph page's index. //! Get the last glyph page's index.
u32 getLastGlyphPageIndex() const { return Glyph_Pages.size() - 1; } u32 getLastGlyphPageIndex() const { return Glyph_Pages.size() - 1; }
@ -328,16 +309,13 @@ namespace gui
//! Create corresponding character's software image copy from the font, //! Create corresponding character's software image copy from the font,
//! so you can use this data just like any ordinary video::IImage. //! so you can use this data just like any ordinary video::IImage.
//! \param ch The character you need //! \param ch The character you need
virtual video::IImage* createTextureFromChar(const char32_t& ch); video::IImage* createTextureFromChar(const char32_t& ch);
//! This function is for debugging mostly. If the page doesn't exist it returns zero. //! This function is for debugging mostly. If the page doesn't exist it returns zero.
//! \param page_index Simply return the texture handle of a given page index. //! \param page_index Simply return the texture handle of a given page index.
virtual video::ITexture* getPageTextureByIndex(const u32& page_index) const; video::ITexture* getPageTextureByIndex(const u32& page_index) const;
//! Add a list of scene nodes generated by putting font textures on the 3D planes. inline video::IVideoDriver *getDriver() const { return Driver; }
virtual core::array<scene::ISceneNode*> addTextSceneNode
(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent = 0,
const video::SColor& color = video::SColor(255, 0, 0, 0), bool center = false );
inline s32 getAscender() const { return font_metrics.ascender; } inline s32 getAscender() const { return font_metrics.ascender; }
@ -355,8 +333,6 @@ namespace gui
static FT_Library c_library; static FT_Library c_library;
static std::map<io::path, SGUITTFace*> c_faces; static std::map<io::path, SGUITTFace*> c_faces;
static bool c_libraryLoaded; static bool c_libraryLoaded;
static scene::IMesh* shared_plane_ptr_;
static scene::SMesh shared_plane_;
// Helper functions for the same-named public member functions above // Helper functions for the same-named public member functions above
// (Since std::u32string is nicer to work with than wchar_t *) // (Since std::u32string is nicer to work with than wchar_t *)
@ -379,20 +355,11 @@ namespace gui
if (useMonochrome()) load_flags |= FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO; if (useMonochrome()) load_flags |= FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO;
else load_flags |= FT_LOAD_TARGET_NORMAL; else load_flags |= FT_LOAD_TARGET_NORMAL;
} }
u32 getWidthFromCharacter(wchar_t c) const;
u32 getWidthFromCharacter(char32_t c) const; u32 getWidthFromCharacter(char32_t c) const;
u32 getHeightFromCharacter(wchar_t c) const;
u32 getHeightFromCharacter(char32_t c) const; u32 getHeightFromCharacter(char32_t c) const;
u32 getGlyphIndexByChar(wchar_t c) const;
u32 getGlyphIndexByChar(char32_t c) const; u32 getGlyphIndexByChar(char32_t c) const;
core::vector2di getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const;
core::vector2di getKerning(const char32_t thisLetter, const char32_t previousLetter) const; core::vector2di getKerning(const char32_t thisLetter, const char32_t previousLetter) const;
core::dimension2d<u32> getDimensionUntilEndOfLine(const wchar_t* p) const;
void createSharedPlane();
irr::IrrlichtDevice* Device;
gui::IGUIEnvironment* Environment;
video::IVideoDriver* Driver; video::IVideoDriver* Driver;
io::path filename; io::path filename;
FT_Face tt_face; FT_Face tt_face;

View file

@ -74,7 +74,7 @@ void StaticText::draw()
updateText(); updateText();
core::rect<s32> r = frameRect; core::rect<s32> r = frameRect;
s32 height_line = font->getDimension(L"A").Height + font->getKerningHeight(); s32 height_line = font->getDimension(L"A").Height + font->getKerning(L'A').Y;
s32 height_total = height_line * BrokenText.size(); s32 height_total = height_line * BrokenText.size();
if (VAlign == EGUIA_CENTER && WordWrap) if (VAlign == EGUIA_CENTER && WordWrap)
{ {
@ -546,7 +546,7 @@ s32 StaticText::getTextHeight() const
return 0; return 0;
if (WordWrap) { if (WordWrap) {
s32 height = font->getDimension(L"A").Height + font->getKerningHeight(); s32 height = font->getDimension(L"A").Height + font->getKerning(L'A').Y;
return height * BrokenText.size(); return height * BrokenText.size();
} }
// There may be intentional new lines without WordWrap // There may be intentional new lines without WordWrap

View file

@ -88,12 +88,11 @@ void ItemStackMetadata::deSerialize(std::istream &is)
void ItemStackMetadata::updateToolCapabilities() void ItemStackMetadata::updateToolCapabilities()
{ {
if (contains(TOOLCAP_KEY)) { if (contains(TOOLCAP_KEY)) {
toolcaps_overridden = true;
toolcaps_override = ToolCapabilities(); toolcaps_override = ToolCapabilities();
std::istringstream is(getString(TOOLCAP_KEY)); std::istringstream is(getString(TOOLCAP_KEY));
toolcaps_override.deserializeJson(is); toolcaps_override->deserializeJson(is);
} else { } else {
toolcaps_overridden = false; toolcaps_override = std::nullopt;
} }
} }

View file

@ -15,8 +15,7 @@ class IItemDefManager;
class ItemStackMetadata : public SimpleMetadata class ItemStackMetadata : public SimpleMetadata
{ {
public: public:
ItemStackMetadata(): ItemStackMetadata()
toolcaps_overridden(false)
{} {}
// Overrides // Overrides
@ -29,7 +28,7 @@ public:
const ToolCapabilities &getToolCapabilities( const ToolCapabilities &getToolCapabilities(
const ToolCapabilities &default_caps) const const ToolCapabilities &default_caps) const
{ {
return toolcaps_overridden ? toolcaps_override : default_caps; return toolcaps_override.has_value() ? *toolcaps_override : default_caps;
} }
void setToolCapabilities(const ToolCapabilities &caps); void setToolCapabilities(const ToolCapabilities &caps);
@ -40,7 +39,6 @@ public:
return wear_bar_override; return wear_bar_override;
} }
void setWearBarParams(const WearBarParams &params); void setWearBarParams(const WearBarParams &params);
void clearWearBarParams(); void clearWearBarParams();
@ -48,7 +46,6 @@ private:
void updateToolCapabilities(); void updateToolCapabilities();
void updateWearBarParams(); void updateWearBarParams();
bool toolcaps_overridden; std::optional<ToolCapabilities> toolcaps_override;
ToolCapabilities toolcaps_override;
std::optional<WearBarParams> wear_bar_override; std::optional<WearBarParams> wear_bar_override;
}; };

Some files were not shown because too many files have changed in this diff Show more