pas2js/demo/webgl/Pas2JS_WebGL_OBJ.pas
2019-03-16 16:54:19 +00:00

154 lines
3.8 KiB
ObjectPascal

program Pas2JS_WebGL_OBJ;
uses
Types, Mat4, MemoryBuffer, GLUtils, GLTypes, SysUtils,
BrowserConsole, Web, WebGL, JS, Math;
var
gl: TJSWebGLRenderingContext;
shader: TShader;
projTransform: TMat4;
viewTransform: TMat4;
modelTransform: TMat4;
var
dragonModel: TModel = nil;
rotateAngle: double = 0;
deltaTime: TJSFloat32 = 0;
nextTime: TJSFloat32 = 0;
procedure DrawCanvas;
begin
gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT);
if dragonModel <> nil then
begin
modelTransform := TMat4.Identity;
modelTransform := modelTransform.Multiply(TMat4.RotateY(DegToRad(rotateAngle)));
shader.SetUniformMat4('modelTransform', modelTransform);
dragonModel.Draw;
end;
end;
procedure AnimateCanvas(time: TJSDOMHighResTimeStamp);
var
now: TJSFloat32;
begin
now := time * 0.001;
deltaTime := now - nextTime;
nextTime := now;
rotateAngle := rotateAngle + (20 * deltaTime);
DrawCanvas;
window.requestAnimationFrame(@AnimateCanvas);
end;
procedure StartAnimatingCanvas;
begin
window.requestAnimationFrame(@AnimateCanvas);
end;
type
TModelLoader = class
public
constructor Create (context: TJSWebGLRenderingContext; path: string);
private
gl: TJSWebGLRenderingContext;
request: TJSXMLHttpRequest;
procedure HandleLoaded;
end;
procedure TModelLoader.HandleLoaded;
var
data: TModelData;
begin
if (request.readyState = 4) and (request.status = 200) and (length(request.responseText) > 0) then
begin
data := LoadOBJFile(TJSString(request.responseText));
dragonModel := TModel.Create(gl, data);
StartAnimatingCanvas;
end;
end;
constructor TModelLoader.Create (context: TJSWebGLRenderingContext; path: string);
begin
gl := context;
request := TJSXMLHttpRequest.new;
request.open('GET', path);
request.overrideMimeType('application/text');
request.onreadystatechange := TJSOnReadyStateChangeHandler(@HandleLoaded);
request.send;
end;
var
canvas: TJSHTMLCanvasElement;
vertexShaderSource: string;
fragmentShaderSource: string;
begin
// make webgl context
canvas := TJSHTMLCanvasElement(document.createElement('canvas'));
canvas.width := 600;
canvas.height := 600;
document.body.appendChild(canvas);
gl := TJSWebGLRenderingContext(canvas.getContext('webgl'));
if gl = nil then
begin
writeln('failed to load webgl!');
exit;
end;
// create shaders from source in html
// TODO: move these to .glsl files so error messages make more sense
// and give valid line numbers
vertexShaderSource := document.getElementById('vertex.glsl').textContent;
fragmentShaderSource := document.getElementById('fragment.glsl').textContent;
shader := TShader.Create(gl, vertexShaderSource, fragmentShaderSource);
shader.Compile;
shader.BindAttribLocation(0, 'in_position');
shader.BindAttribLocation(1, 'in_texCoord');
shader.BindAttribLocation(2, 'in_normal');
shader.Link;
shader.Use;
// prepare context
gl.clearColor(0.9, 0.9, 0.9, 1);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.Enable(gl.CULL_FACE);
gl.CullFace(gl.BACK);
// set projection transform
projTransform := TMat4.Perspective(60.0, canvas.width / canvas.height, 0.1, 2000);
shader.SetUniformMat4('projTransform', projTransform);
// set view transform
viewTransform := TMat4.Identity;
viewTransform := viewTransform.Multiply(TMat4.Translate(0, -3, -20));
shader.SetUniformMat4('viewTransform', viewTransform);
// NOTE: webgl glsl doesn't have the inverse function
// so we need to do this here
shader.SetUniformMat4('inverseViewTransform', viewTransform.Inverse);
// lighting
shader.SetUniformVec3('lightPosition', V3(0, 0, 25));
shader.SetUniformVec3('lightColor', V3(1, 1, 1));
// model material
shader.SetUniformFloat('shineDamper', 10);
shader.SetUniformFloat('reflectivity', 1);
// load obj file
TModelLoader.Create(gl, 'res/dragon.obj');
end.