You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

309 lines
8.1 KiB
PHP

<?php
/** @noinspection PhpUnused */
namespace Cijber\GraphicsToolkit;
use Cijber\GraphicsToolkit\Consts\GLConsts;
use FFI;
use FFI\CData;
use ReflectionClass;
use ReflectionMethod;
use RuntimeException;
/**
* @method static void bindVertexArray(int $VAO)
* @method static void bindBuffer(int $type, int $VBO)
* @method static void bufferData(int $type, int $size, $data, int $management)
* @method static void viewport(int $x, int $y, int $width, int $height)
* @method static void enableVertexAttribArray(int $VAO)
* @method static void drawArrays(int $type, int $index, int $count)
* @method static void useProgram($shaderProgram)
* @method static void clear(int $type)
* @method static void clearColor(float $red, float $green, float $blue, float $alpha)
* @method static int createShader(int $type)
* @method static void compileShader(int $shader)
* @method static void attachShader(int $program, int $shader)
* @method static int createProgram()
* @method static void linkProgram(int $program)
* @method static void deleteShader(int $shader)
* @method static int getError()
* @method static void enable(int $flag)
* @method static void debugMessageCallback(callable $callback, $userParam)
* @method static void debugMessageControl(int $source, int $type, int $severity, int $count, $null, bool $enabled)
* @method static void blendFunc(int $sfactor, int $bfactor)
*/
class GL extends GLConsts {
public static string $lastCall;
public static FFI $ffi;
/**
* @var array|callable[]
*/
public static array $calls;
/**
* @GLIgnore
*/
public static function init() {
static::$ffi = FFI::load(__DIR__ . "/../headers/gl_generated.h");
$reflection = new ReflectionClass(__CLASS__);
$methods = $reflection->getMethods(
ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_STATIC
);
foreach ($methods as $method) {
if (str_contains($method->getDocComment() ?: "", "@GLIgnore")) {
continue;
}
static::$calls[$method->name] = static::getProcAddress(
$method->name
);
}
preg_match_all(
":@method static [a-zA-Z]+ ([a-zA-Z0-9]+):",
$reflection->getDocComment(),
$matches
);
foreach ($matches[1] as $match) {
static::$calls[$match] = static::getProcAddress($match);
}
}
/**
* @GLIgnore
*/
private static function getProcAddress($name): CData {
$funcname = 'gl' . ucfirst($name);
$typename = 'FUNC_' . $funcname;
$funcstring = static::$ffi->new(FFI::arrayType(static::$ffi->type('GLubyte'), [strlen($funcname) + 1]));
FFI::memcpy($funcstring, $funcname, strlen($funcname));
$funcstring[strlen($funcname)] = "\0";
/** @var CData $proc */
/** @noinspection PhpUndefinedMethodInspection */
$proc = static::$ffi->glXGetProcAddress(
static::$ffi->cast("GLubyte*", $funcstring)
);
/** @noinspection PhpUndefinedMethodInspection */
if ($proc === null || FFI::isNull($proc)) {
throw new RuntimeException("GL function $name doesn't exist");
}
return static::$ffi->cast($typename, $proc);
}
public static function genBuffers(int $amount, &$ids) {
if ($amount > 1) {
$ids = array_fill(0, $amount, 0);
}
if (is_array($ids)) {
$arr = &$ids;
} else {
$arr = [&$ids];
}
$items = static::$ffi->new("GLuint[$amount]");
(static::$calls['genBuffers'])($amount, $items);
for ($i = 0; $i < $amount; $i++) {
$arr[$i] = $items[$i];
}
}
public static function genVertexArrays(int $amount, &$ids) {
if ($amount > 1) {
$ids = array_fill(0, $amount, 0);
}
if (is_array($ids)) {
$arr = &$ids;
} else {
$arr = [&$ids];
}
$items = static::$ffi->new("GLuint[$amount]");
(static::$calls['genVertexArrays'])($amount, $items);
for ($i = 0; $i < $amount; $i++) {
$arr[$i] = $items[$i];
}
}
public static function genTextures(int $amount, &$ids) {
if ($amount > 1) {
$ids = array_fill(0, $amount, 0);
}
if (is_array($ids)) {
$arr = &$ids;
} else {
$arr = [&$ids];
}
$items = static::$ffi->new("GLuint[$amount]");
(static::$calls['genTextures'])($amount, $items);
for ($i = 0; $i < $amount; $i++) {
$arr[$i] = $items[$i];
}
}
/**
* @GLIgnore
*/
public static function __callStatic($name, $arguments) {
if ( ! isset(static::$calls[$name])) {
static::$calls[$name] = static::getProcAddress($name);
}
static::$lastCall = $name;
return (static::$calls[$name])(...$arguments);
}
public static function vertexAttribPointer(
int $index,
int $size,
int $type,
bool $normalized,
int $stride,
int $pointer
) {
$pointerC = static::$ffi->new('long long');
$pointerC->cdata = $pointer;
$p = static::$ffi->cast(
'void*',
$pointerC
);
(static::$calls['vertexAttribPointer'])(
$index,
$size,
$type,
$normalized ? 1 : 0,
$stride,
$p,
);
}
public static function getShaderiv(
int $shader,
int $type,
&$params
) {
$paramsC = static::$ffi->new('GLint');
(static::$calls['getShaderiv'])(
$shader,
$type,
FFI::addr($paramsC)
);
$params = $paramsC->cdata;
}
public static function getProgramiv(
int $program,
int $type,
&$params
) {
$paramsC = static::$ffi->new('GLint');
(static::$calls['getProgramiv'])(
$program,
$type,
FFI::addr($paramsC)
);
$params = $paramsC->cdata;
}
public static function getProgramInfoLog(
int $program,
&$log,
int $maxLength = 512
) {
$logC = static::$ffi->new("char[$maxLength]");
$actualLength = static::$ffi->new("GLsizei");
(static::$calls['getProgramInfoLog'])(
$program,
$maxLength,
FFI::addr($actualLength),
$logC,
);
$log = FFI::string($logC, $actualLength->cdata);
}
public static function getShaderInfoLog(
int $shader,
null|string &$log = null,
int $maxLength = 512
) {
$logC = static::$ffi->new(FFI::arrayType(FFI::type("char"), [$maxLength]));
$actualLength = static::$ffi->new("GLsizei");
(static::$calls['getShaderInfoLog'])(
$shader,
$maxLength,
FFI::addr($actualLength),
$logC,
);
$log = FFI::string($logC, $actualLength->cdata);
}
public static function shaderSource(
int $shader,
int $count,
string $source,
?array $offsets
) {
$item = null;
if (is_array($offsets) && count($offsets) > 0) {
$item = static::$ffi->new('GLint[' . count($offsets) . ']');
for ($i = 0; $i < count($offsets); $i++) {
$item[$i] = $offsets[$i];
}
}
$sourceConst = Utils::constString($source . "\0");
(static::$calls['shaderSource'])(
$shader,
$count,
FFI::addr($sourceConst),
$item,
);
}
/**
* @GLIgnore
*/
public static function getInteger(int $item): int {
return static::getIntegerv($item)[0];
}
public static function getIntegerv(int $item, int $values = 1): array {
$t = static::$ffi->new(FFI::arrayType(static::$ffi->type("GLint"), [$values]));
(static::$calls['getIntegerv'])($item, $t);
$arr = [];
for ($i = 0; $i < $values; $i++) {
$arr[$i] = $t[$i];
}
return $arr;
}
}