In prep for the 1.1 minor release a few final issues were addressed:

New Features
- Full pinch, zoom and rotate with two fingers is now supported

Bug fixes
- Dropdown menus will now correctly appear on top of canvases in the z-order
- Dropdown menus will align on the opposite side if not doing so would result
  appearing outside the viewbox
This commit is contained in:
Matthew A Johnson 2023-03-20 11:24:46 +00:00 коммит произвёл GitHub
Родитель d9b4726a1a
Коммит 892b2c4996
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
24 изменённых файлов: 1524 добавлений и 1676 удалений

Просмотреть файл

@ -1,5 +1,16 @@
# Changelog
## [2023-03-13 - Version 1.1.0](https://github.com/microsoft/scenepic/releases/tag/v1.1.0)
As part of this 1.1 minor release a few final issues were addressed:
New Features
- Full pinch, zoom and rotate with two fingers is now supported
Bug fixes
- Dropdown menus will now correctly appear on top of canvases in the z-order
- Dropdown menus will align on the opposite side if not doing so would result
appearing outside the viewbox
## [2023-03-13 - Version 1.0.19](https://github.com/microsoft/scenepic/releases/tag/v1.0.19)
Point release adding pan/zoom functionality to 2D canvases.

Просмотреть файл

@ -1 +1,9 @@
Point release adding pan/zoom functionality to 2D canvases.
As part of this 1.1 minor release a few final issues were addressed:
New Features
- Full pinch, zoom and rotate with two fingers is now supported
Bug fixes
- Dropdown menus will now correctly appear on top of canvases in the z-order
- Dropdown menus will align on the opposite side if not doing so would result
appearing outside the viewbox

Просмотреть файл

@ -1 +1 @@
1.0.19
1.1.0

Просмотреть файл

@ -22,7 +22,7 @@ copyright = "2021, Microsoft"
author = "ScenePic Team"
# The full version, including alpha/beta/rc tags
release = "1.0.19"
release = "1.1.0"
# -- General configuration ---------------------------------------------------

214
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "scenepic",
"version": "1.0.19",
"version": "1.1.0",
"description": "3D Visualization Made Easy",
"author": "ScenePic Team",
"license": "MIT",

Просмотреть файл

@ -0,0 +1,60 @@
"""Test script for dropdown CSS"""
import random
from typing import List
import numpy as np
import scenepic as sp
def _create_cubes(scene: sp.Scene, num_cubes: int) -> List[sp.Mesh]:
meshes = []
for i in range(num_cubes):
color = np.random.rand(3)
mesh: sp.Mesh = scene.create_mesh(layer_id="cube{}".format(i), shared_color=color)
mesh.add_cube()
meshes.append(mesh)
return meshes
def _add_objects(canvas: sp.Canvas3D, cubes: List[sp.Mesh]):
frame = canvas.create_frame()
num_cubes = np.random.randint(5, 10)
choices = np.random.choice(len(cubes), num_cubes, replace=False)
for index in choices:
pos = np.random.rand(3) * 2 - 1
frame.add_mesh(cubes[index], sp.Transforms.Translate(pos))
def _add_circles(canvas: sp.Canvas2D):
frame: sp.Frame2D = canvas.create_frame()
num_circles = np.random.randint(5, 10)
for i in range(num_circles):
pos = np.random.rand(2) * 100
radius = np.random.random() * 10
color = np.random.rand(3)
frame.add_circle(pos[0], pos[1], radius, fill_color=color, layer_id="circle{}".format(i))
def _main():
scene = sp.Scene()
cubes = _create_cubes(scene, 20)
for row in range(1, 5):
for col in range(1, 5):
canvasID = "canvas" + str(row) + str(col)
if random.random() < 0.5:
canvas = scene.create_canvas_3d(canvasID, 100, 100)
_add_objects(canvas, cubes)
else:
canvas = scene.create_canvas_2d(canvasID, 100, 100)
_add_circles(canvas)
scene.place(canvasID, str(row), str(col))
scene.grid("400px", "auto auto auto auto", "auto auto auto auto")
scene.save_as_html("overlap.html")
if __name__ == "__main__":
_main()

Просмотреть файл

@ -3,4 +3,4 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
__version__ = "1.0.19"
__version__ = "1.1.0"

Просмотреть файл

@ -79,6 +79,7 @@ var css = `
top: 5px;
right: 5px;
user-select: none;
z-index: 10;
}
.scenepic-dropdown:after {
@ -99,6 +100,14 @@ var css = `
margin-top: -9px;
}
.scenepic-left-table {
left: 1px
}
.scenepic-right-table {
right: -1px
}
.scenepic-dropdown-table {
list-style: none !important;
margin: 0 !important;
@ -106,7 +115,6 @@ var css = `
position: absolute;
top: 100%;
border: inherit;
right: -1px;
visibility: hidden;
background-color: white;
pointer-events: none;
@ -330,15 +338,14 @@ input.scenepic-volume-slider {
var initialized = false;
export default function InitializeCSSStyles()
{
if (initialized) return;
var head = document.getElementsByTagName("head")[0];
var style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode(css));
head.appendChild(style);
export default function InitializeCSSStyles() {
if (initialized) return;
initialized = true;
var head = document.getElementsByTagName("head")[0];
var style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode(css));
head.appendChild(style);
initialized = true;
}

Просмотреть файл

@ -1,30 +1,26 @@
import Misc from "./Misc"
import {ObjectCache, CanvasBase} from "./CanvasBase"
import { ObjectCache, CanvasBase } from "./CanvasBase"
import { mat3, vec2 } from "gl-matrix";
abstract class Primitive
{
readonly layerId : string;
abstract class Primitive {
readonly layerId: string;
constructor(layerId: string)
{
constructor(layerId: string) {
this.layerId = layerId;
}
GetObjectCacheId() : string { return null; }
GetObjectCacheId(): string { return null; }
abstract Draw(HTMLCanvasElement, CanvasRenderingContext2D, ObjectCache);
}
class LinesPrimitive extends Primitive
{
readonly strokeStyle : string;
readonly lineWidth : number;
readonly fillStyle : string;
readonly coordinates : Float32Array;
readonly closePath : boolean;
class LinesPrimitive extends Primitive {
readonly strokeStyle: string;
readonly lineWidth: number;
readonly fillStyle: string;
readonly coordinates: Float32Array;
readonly closePath: boolean;
constructor(strokeStyle : string, lineWidth : number, fillStyle : string, coordinates : Float32Array, closePath : boolean, layerId : string)
{
constructor(strokeStyle: string, lineWidth: number, fillStyle: string, coordinates: Float32Array, closePath: boolean, layerId: string) {
super(layerId);
this.strokeStyle = strokeStyle;
this.lineWidth = lineWidth;
@ -32,22 +28,19 @@ class LinesPrimitive extends Primitive
this.coordinates = coordinates;
this.closePath = closePath;
}
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
{
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
context.beginPath();
context.moveTo(this.coordinates[0] * window.devicePixelRatio, this.coordinates[1] * window.devicePixelRatio);
for(var i = 0; i < this.coordinates.length; i += 2)
for (var i = 0; i < this.coordinates.length; i += 2)
context.lineTo(this.coordinates[i] * window.devicePixelRatio, this.coordinates[i + 1] * window.devicePixelRatio);
if (this.closePath)
context.closePath();
if (this.fillStyle != null)
{
if (this.fillStyle != null) {
context.fillStyle = this.fillStyle;
context.fill();
}
if (this.strokeStyle != null && this.lineWidth > 0)
{
if (this.strokeStyle != null && this.lineWidth > 0) {
context.strokeStyle = this.strokeStyle;
context.lineWidth = this.lineWidth;
context.stroke();
@ -55,16 +48,14 @@ class LinesPrimitive extends Primitive
}
}
class CirclePrimitive extends Primitive
{
readonly strokeStyle : string;
readonly lineWidth : number;
readonly fillStyle : string;
readonly center : Float32Array;
readonly radius : number;
class CirclePrimitive extends Primitive {
readonly strokeStyle: string;
readonly lineWidth: number;
readonly fillStyle: string;
readonly center: Float32Array;
readonly radius: number;
constructor(strokeStyle : string, lineWidth : number, fillStyle : string, center : Float32Array, radius : number, layerId : string)
{
constructor(strokeStyle: string, lineWidth: number, fillStyle: string, center: Float32Array, radius: number, layerId: string) {
super(layerId);
this.strokeStyle = strokeStyle;
this.lineWidth = lineWidth;
@ -72,18 +63,15 @@ class CirclePrimitive extends Primitive
this.center = center;
this.radius = radius;
}
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
{
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
context.beginPath();
context.arc(this.center[0] * window.devicePixelRatio, this.center[1] * window.devicePixelRatio, this.radius * window.devicePixelRatio, 0.0, 2.0 * Math.PI, false);
if (this.fillStyle != null)
{
if (this.fillStyle != null) {
context.fillStyle = this.fillStyle;
context.fill();
}
if (this.strokeStyle != null && this.lineWidth > 0)
{
if (this.strokeStyle != null && this.lineWidth > 0) {
context.strokeStyle = this.strokeStyle;
context.lineWidth = this.lineWidth;
context.stroke();
@ -91,16 +79,14 @@ class CirclePrimitive extends Primitive
}
}
class TextPrimitive extends Primitive
{
readonly text : string;
readonly fillStyle : string;
readonly sizeInPixels : number;
readonly fontFamily : string;
readonly position : Float32Array;
class TextPrimitive extends Primitive {
readonly text: string;
readonly fillStyle: string;
readonly sizeInPixels: number;
readonly fontFamily: string;
readonly position: Float32Array;
constructor(text : string, fillStyle : string, sizeInPixels : number, fontFamily : string, position : Float32Array, layerId : string)
{
constructor(text: string, fillStyle: string, sizeInPixels: number, fontFamily: string, position: Float32Array, layerId: string) {
super(layerId);
this.text = text;
this.fillStyle = fillStyle;
@ -109,8 +95,7 @@ class TextPrimitive extends Primitive
this.position = position;
}
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
{
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
context.fillStyle = this.fillStyle;
let sizeInPixels = this.sizeInPixels * window.devicePixelRatio;
let left = this.position[0] * window.devicePixelRatio;
@ -121,16 +106,14 @@ class TextPrimitive extends Primitive
}
}
class ImagePrimitive extends Primitive
{
readonly imageId : string;
readonly positionType : string;
readonly position : Float32Array
readonly scale : number;
readonly smoothed : boolean;
class ImagePrimitive extends Primitive {
readonly imageId: string;
readonly positionType: string;
readonly position: Float32Array
readonly scale: number;
readonly smoothed: boolean;
constructor(imageId : string, positionType : string, position : Float32Array, scale : number, smoothed : boolean, layerId : string)
{
constructor(imageId: string, positionType: string, position: Float32Array, scale: number, smoothed: boolean, layerId: string) {
super(layerId);
this.imageId = imageId;
this.positionType = positionType;
@ -139,13 +122,11 @@ class ImagePrimitive extends Primitive
this.smoothed = smoothed;
}
GetObjectCacheId()
{
GetObjectCacheId() {
return this.imageId;
}
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
{
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
// Get current image
var currentImage = objectCache.GetObject(this.imageId);
if (currentImage == null) return; // Indicates image not available (e.g. not completely loaded yet)
@ -157,8 +138,7 @@ class ImagePrimitive extends Primitive
var centered = true;
var scaleX = canvas.clientWidth / currentImage.naturalWidth;
var scaleY = canvas.clientHeight / currentImage.naturalHeight;
switch(this.positionType)
{
switch (this.positionType) {
case "fill":
scaleX = scaleY = Math.max(scaleX, scaleY);
break;
@ -184,16 +164,14 @@ class ImagePrimitive extends Primitive
}
}
class VideoPrimitive extends Primitive
{
readonly videoId : string;
readonly positionType : string;
readonly position : Float32Array
readonly scale : number;
readonly smoothed : boolean;
class VideoPrimitive extends Primitive {
readonly videoId: string;
readonly positionType: string;
readonly position: Float32Array
readonly scale: number;
readonly smoothed: boolean;
constructor(videoId : string, positionType : string, position : Float32Array, scale : number, smoothed : boolean, layerId : string)
{
constructor(videoId: string, positionType: string, position: Float32Array, scale: number, smoothed: boolean, layerId: string) {
super(layerId);
this.videoId = videoId;
this.positionType = positionType;
@ -202,15 +180,13 @@ class VideoPrimitive extends Primitive
this.smoothed = smoothed;
}
GetObjectCacheId()
{
GetObjectCacheId() {
return this.videoId;
}
Draw(canvas : HTMLCanvasElement, context : CanvasRenderingContext2D, objectCache : ObjectCache)
{
Draw(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, objectCache: ObjectCache) {
// Get current image
let currentVideo : HTMLVideoElement = objectCache.GetObject(this.videoId);
let currentVideo: HTMLVideoElement = objectCache.GetObject(this.videoId);
if (currentVideo == null) return; // Indicates video not available (e.g. not completely loaded yet)
// Turn off image smoothing
@ -220,8 +196,7 @@ class VideoPrimitive extends Primitive
let centered = true;
let scaleX = canvas.clientWidth / currentVideo.videoWidth;
let scaleY = canvas.clientHeight / currentVideo.videoHeight;
switch(this.positionType)
{
switch (this.positionType) {
case "fill":
scaleX = scaleY = Math.max(scaleX, scaleY);
break;
@ -248,30 +223,31 @@ class VideoPrimitive extends Primitive
}
export default class Canvas2D extends CanvasBase
{
context : CanvasRenderingContext2D = null; // Rendering context
export default class Canvas2D extends CanvasBase {
context: CanvasRenderingContext2D = null; // Rendering context
backgroundStyle : string; // Background color
backgroundStyle: string; // Background color
// Dictionary from primitiveId to layer settings (wireframe, filled, opacity)
layerSettings : {[layerId: string]: {[key: string]: number|boolean}} = {};
layerIds : string[] = [];
layerSettings: { [layerId: string]: { [key: string]: number | boolean } } = {};
layerIds: string[] = [];
globalFill = true;
globalOpacity = 1.0;
framePrimitives : Primitive[][] = []; // The primitives for each frame [frameIndex]
framePrimitives: Primitive[][] = []; // The primitives for each frame [frameIndex]
currentPrimitives : Primitive[] = null; // The primitives for the currently displayed frame
currentPrimitives: Primitive[] = null; // The primitives for the currently displayed frame
frameCoordinates : Float32Array[] = []; // The coordinates for each frame [frameIndex]
frameCoordinates: Float32Array[] = []; // The coordinates for each frame [frameIndex]
scale : number;
focusPoint : vec2;
initFocusPoint : vec2;
center: vec2;
scale: number;
angle: number;
focusPoint: vec2;
focusPointDelta: vec2;
transform: mat3;
constructor(canvasId : string, public frameRate : number, public width : number, public height : number, objectCache : ObjectCache, public SetStatus : (status : string) => void, public SetWarning : (message : string) => void, public RequestRedraw : () => void, public ReportFrameIdChange : (canvasId : string, frameId : string) => void)
{
constructor(canvasId: string, public frameRate: number, public width: number, public height: number, objectCache: ObjectCache, public SetStatus: (status: string) => void, public SetWarning: (message: string) => void, public RequestRedraw: () => void, public ReportFrameIdChange: (canvasId: string, frameId: string) => void) {
// Base class constructor
super(canvasId, frameRate, width, height, objectCache, SetStatus, SetWarning, RequestRedraw, ReportFrameIdChange);
@ -279,6 +255,8 @@ export default class Canvas2D extends CanvasBase
this.context = this.htmlCanvas.getContext('2d');
this.backgroundStyle = "#000000";
this.center = vec2.fromValues(width, height);
vec2.scale(this.center, this.center, window.devicePixelRatio / 2);
this.ResetView();
@ -287,15 +265,14 @@ export default class Canvas2D extends CanvasBase
}
// Adds UI for the control of certain layers
SetLayerSettings(layerSettings : any)
{
SetLayerSettings(layerSettings: any) {
// Delete any previous controls
while (this.dropdownTable.childElementCount > 2)
this.dropdownTable.removeChild(this.dropdownTable.lastChild);
this.layerSettings = layerSettings;
this.layerIds = [null];
for(let layerId in layerSettings){
for (let layerId in layerSettings) {
this.layerIds.push(layerId);
}
@ -303,8 +280,7 @@ export default class Canvas2D extends CanvasBase
var BorderStyle = "1px solid #cccccc";
var headerRow = document.createElement("tr");
headerRow.style.borderBottom = BorderStyle;
for(var headerName of ["Visible", "Opacity", "Layer Id"])
{
for (var headerName of ["Visible", "Opacity", "Layer Id"]) {
var headerItem = document.createElement("th");
headerItem.className = "scenepic-dropdown-header";
headerItem.innerHTML = headerName;
@ -313,8 +289,7 @@ export default class Canvas2D extends CanvasBase
this.dropdownTable.appendChild(headerRow);
// Create row helper function
var createRow = (id, label) =>
{
var createRow = (id, label) => {
// Create fill checkbox
var checkboxFill = document.createElement("input");
checkboxFill.type = "checkbox";
@ -338,14 +313,12 @@ export default class Canvas2D extends CanvasBase
return [checkboxFill, sliderOpacity, labelLayer];
};
var addRow = (rowItems, border) =>
{
var addRow = (rowItems, border) => {
// Create table row
var tr = document.createElement("tr");
if (border)
tr.style.borderTop = BorderStyle;
var addControl = (el, className) =>
{
var addControl = (el, className) => {
var td = document.createElement("td");
td.appendChild(el);
td.className = className;
@ -362,8 +335,7 @@ export default class Canvas2D extends CanvasBase
addRow(createRow("<<<GLOBAL>>>", "Global"), true);
}
ShowLayerFilled(layerId : string) : boolean
{
ShowLayerFilled(layerId: string): boolean {
if (layerId == "<<<GLOBAL>>>")
return this.globalFill;
if (layerId == null)
@ -375,28 +347,24 @@ export default class Canvas2D extends CanvasBase
return true;
}
ToggleLayerFilled(index: number)
{
if(index >= this.layerIds.length) {
ToggleLayerFilled(index: number) {
if (index >= this.layerIds.length) {
return
}
let layerId = this.layerIds[index];
let filled = this.ShowLayerFilled(layerId);
this.SetLayerFilled(layerId, !filled);
}
}
SetLayerFilled(layerId : string, filled : boolean)
{
SetLayerFilled(layerId: string, filled: boolean) {
if (layerId == null)
return;
if (layerId == "<<<GLOBAL>>>")
{
if (layerId == "<<<GLOBAL>>>") {
this.globalFill = filled;
}
else
{
else {
if (!(layerId in this.layerSettings))
this.layerSettings[layerId] = {};
@ -407,8 +375,7 @@ export default class Canvas2D extends CanvasBase
this.PrepareBuffers();
}
GetLayerOpacity(layerId : string) : number
{
GetLayerOpacity(layerId: string): number {
if (layerId == "<<<GLOBAL>>>")
return this.globalOpacity;
if (layerId == null || !(layerId in this.layerSettings) || !("opacity" in this.layerSettings[layerId]))
@ -417,17 +384,14 @@ export default class Canvas2D extends CanvasBase
return <number>this.layerSettings[layerId]["opacity"];
}
SetLayerOpacity(layerId : string, opacity : number)
{
SetLayerOpacity(layerId: string, opacity: number) {
if (layerId == null)
return;
if (layerId == "<<<GLOBAL>>>")
{
if (layerId == "<<<GLOBAL>>>") {
this.globalOpacity = opacity;
}
else
{
else {
if (!(layerId in this.layerSettings))
this.layerSettings[layerId] = {};
this.layerSettings[layerId]["opacity"] = opacity;
@ -435,10 +399,9 @@ export default class Canvas2D extends CanvasBase
// Prepare buffers and force redraw
this.PrepareBuffers();
}
}
GetLayerRenderOrder(layerId : string) : number
{
GetLayerRenderOrder(layerId: string): number {
if (layerId == null || !(layerId in this.layerSettings) || !("renderOrder" in this.layerSettings[layerId]))
return -1e3; // Backmost layer - bit of a hack
else
@ -446,10 +409,8 @@ export default class Canvas2D extends CanvasBase
}
// Execute a single canvas command
ExecuteCanvasCommand(command : any)
{
switch(command["CommandType"])
{
ExecuteCanvasCommand(command: any) {
switch (command["CommandType"]) {
case "SetBackgroundStyle":
this.backgroundStyle = String(command["Value"]);
break;
@ -470,32 +431,31 @@ export default class Canvas2D extends CanvasBase
}
}
AddPrimitive(frameIndex : number, primitive : Primitive)
{
if(primitive.layerId == null){
AddPrimitive(frameIndex: number, primitive: Primitive) {
if (primitive.layerId == null) {
this.framePrimitives[frameIndex].push(primitive);
}else{
if(!(primitive.layerId in this.layerSettings)){
} else {
if (!(primitive.layerId in this.layerSettings)) {
this.layerSettings[primitive.layerId] = {};
this.SetLayerSettings(this.layerSettings);
}
let renderOrder = this.GetLayerRenderOrder(primitive.layerId);
let index = 0;
let lastId : string = null;
for(let other of this.framePrimitives[frameIndex]){
if(other.layerId == lastId){
let lastId: string = null;
for (let other of this.framePrimitives[frameIndex]) {
if (other.layerId == lastId) {
index += 1;
continue;
}
if(other.layerId == primitive.layerId){
if (other.layerId == primitive.layerId) {
break;
}
lastId = other.layerId;
let otherOrder = this.GetLayerRenderOrder(other.layerId);
if(renderOrder < otherOrder){
if (renderOrder < otherOrder) {
break;
}
@ -505,41 +465,37 @@ export default class Canvas2D extends CanvasBase
}
if (this.currentFrameIndex == frameIndex)
this.PrepareBuffers();
this.PrepareBuffers();
}
SetCoordinates(frameIndex: number, coordinates: Float32Array)
{
SetCoordinates(frameIndex: number, coordinates: Float32Array) {
this.frameCoordinates[frameIndex] = coordinates;
}
GetCoordinate(frameIndex: number, index: number)
{
GetCoordinate(frameIndex: number, index: number) {
let start = index * 2;
let end = start + 2;
return this.frameCoordinates[frameIndex].slice(start, end);
}
AddLines(frameIndex: number, lines: Uint16Array, style: Uint8Array, width: Float32Array, layerIds: [string, number][])
{
AddLines(frameIndex: number, lines: Uint16Array, style: Uint8Array, width: Float32Array, layerIds: [string, number][]) {
let numLines = width.length;
let layerId = null;
let nextLayer = numLines;
let currentLayer = 0;
if(layerIds != null){
if (layerIds != null) {
layerId = layerIds[0][0];
nextLayer = currentLayer + layerIds[0][1];
}
for(let i=0; i<numLines; ++i)
{
let info = lines.slice(i*3, i*3 + 3);
let coordinates = this.frameCoordinates[frameIndex].slice(info[0]*2, info[1]*2);
for (let i = 0; i < numLines; ++i) {
let info = lines.slice(i * 3, i * 3 + 3);
let coordinates = this.frameCoordinates[frameIndex].slice(info[0] * 2, info[1] * 2);
let closePath = info[2] == 1;
let strokeStyle = Misc.StyleToHtmlHex(style.slice(i*8, i*8 + 4));
let fillStyle = Misc.StyleToHtmlHex(style.slice(i*8 + 4, i*8 + 8));
let strokeStyle = Misc.StyleToHtmlHex(style.slice(i * 8, i * 8 + 4));
let fillStyle = Misc.StyleToHtmlHex(style.slice(i * 8 + 4, i * 8 + 8));
let lineWidth = width[i];
if(i == nextLayer){
if (i == nextLayer) {
currentLayer += 1;
layerId = layerIds[currentLayer][0];
nextLayer = i + layerIds[currentLayer][1]
@ -549,26 +505,24 @@ export default class Canvas2D extends CanvasBase
}
}
AddCircles(frameIndex: number, circles: Float32Array, style: Uint8Array, layerIds: [string, number][])
{
AddCircles(frameIndex: number, circles: Float32Array, style: Uint8Array, layerIds: [string, number][]) {
let numCircles = style.length / 8;
let layerId = null;
let nextLayer = numCircles;
let currentLayer = 0;
if(layerIds != null){
if (layerIds != null) {
layerId = layerIds[0][0];
nextLayer = currentLayer + layerIds[0][1];
}
for(let i=0; i<numCircles; ++i)
{
let info = circles.slice(i*4, i*4 + 4);
for (let i = 0; i < numCircles; ++i) {
let info = circles.slice(i * 4, i * 4 + 4);
let center = info.slice(0, 2);
let radius = info[2];
let lineWidth = info[3];
let strokeStyle = Misc.StyleToHtmlHex(style.slice(i*8, i*8 + 4));
let fillStyle = Misc.StyleToHtmlHex(style.slice(i*8 + 4, i*8 + 8));
if(i == nextLayer){
let strokeStyle = Misc.StyleToHtmlHex(style.slice(i * 8, i * 8 + 4));
let fillStyle = Misc.StyleToHtmlHex(style.slice(i * 8 + 4, i * 8 + 8));
if (i == nextLayer) {
currentLayer += 1;
layerId = layerIds[currentLayer][0];
nextLayer = i + layerIds[currentLayer][1]
@ -579,10 +533,8 @@ export default class Canvas2D extends CanvasBase
}
// Execute a single frame command
ExecuteFrameCommand(command : any, frameIndex : number)
{
switch(command["CommandType"])
{
ExecuteFrameCommand(command: any, frameIndex: number) {
switch (command["CommandType"]) {
case "SetCoordinates":
var coordinates = Misc.Base64ToFloat32Array(command["CoordinateBuffer"])
this.SetCoordinates(frameIndex, coordinates);
@ -640,24 +592,20 @@ export default class Canvas2D extends CanvasBase
}
}
AllocateFrame()
{
AllocateFrame() {
this.framePrimitives.push([]);
this.frameCoordinates.push(null);
}
DeallocateFrame(frameIndex : number)
{
DeallocateFrame(frameIndex: number) {
this.framePrimitives[frameIndex] = [];
this.frameCoordinates[frameIndex] = null;
}
NotifyTextureUpdated(textureId : string)
{
NotifyTextureUpdated(textureId: string) {
}
PrepareBuffers() : void
{
PrepareBuffers(): void {
super.PrepareBuffers();
var currentFrameIndex = this.currentFrameIndex;
@ -666,15 +614,12 @@ export default class Canvas2D extends CanvasBase
var newPrimitives = this.framePrimitives[currentFrameIndex].slice();
// Deal with image caching (only applies if primitives are actually ImagePrimitives or VideoPrimitives)
for(var primitive of newPrimitives)
{
for (var primitive of newPrimitives) {
var cacheId = primitive.GetObjectCacheId();
if (cacheId != null) this.objectCache.AddUser(cacheId);
}
if (this.currentPrimitives != null)
{
for(var primitive of this.currentPrimitives)
{
if (this.currentPrimitives != null) {
for (var primitive of this.currentPrimitives) {
var cacheId = primitive.GetObjectCacheId();
if (cacheId != null) this.objectCache.RemoveUser(cacheId);
}
@ -684,63 +629,66 @@ export default class Canvas2D extends CanvasBase
this.currentPrimitives = newPrimitives;
}
ResetView() : void
{
this.focusPoint = vec2.fromValues(this.width / 2, this.height / 2);
ResetView(): void {
this.focusPointDelta = vec2.create();
this.focusPoint = vec2.copy(vec2.create(), this.center);
this.scale = 1;
this.angle = 0;
this.updateTransform();
this.setTransform();
}
private getTransform() : mat3
{
const focusPixel = vec2.fromValues(
this.htmlCanvas.clientWidth / 2,
this.htmlCanvas.clientHeight / 2)
ComputeCameraTwist(point: vec2, event: PointerEvent) {
const old = this.pointerCoords.get(event.pointerId);
if (old == undefined) {
return 0;
}
const translate = vec2.scale(vec2.create(), this.focusPoint, this.scale);
vec2.subtract(translate, focusPixel, translate);
return mat3.fromValues(
this.scale, 0, 0,
0, this.scale, 0,
translate[0], translate[1], 1)
const dx = point[0] - old[0];
const dy = old[1] - point[1];
let delta = dx;
if (Math.abs(dy) > Math.abs(dx)) {
delta = dy;
}
return 2 * delta / (window.devicePixelRatio * this.htmlCanvas.clientWidth);
}
private setTransform() : void
{
let transform = this.getTransform();
private updateTransform() {
const toFocus = mat3.fromTranslation(mat3.create(), this.focusPoint);
const fromCenter = mat3.fromTranslation(mat3.create(), vec2.negate(vec2.create(), this.center));
const scale = mat3.fromScaling(mat3.create(), vec2.fromValues(this.scale, this.scale));
const rotate = mat3.fromRotation(mat3.create(), this.angle);
this.transform = mat3.create();
mat3.multiply(this.transform, fromCenter, this.transform);
mat3.multiply(this.transform, scale, this.transform);
mat3.multiply(this.transform, rotate, this.transform);
mat3.multiply(this.transform, toFocus, this.transform);
}
private setTransform(): void {
this.context.setTransform(
transform[0], transform[1],
transform[3], transform[4],
transform[6], transform[7])
this.transform[0], this.transform[1],
this.transform[3], this.transform[4],
this.transform[6], this.transform[7])
}
// Render scene method
Render()
{
Render() {
// Clear
this.context.fillStyle = this.backgroundStyle;
const inv = mat3.invert(mat3.create(), this.getTransform());
let x = 0;
let y = 0;
this.context.resetTransform();
let width = this.htmlCanvas.clientWidth * window.devicePixelRatio;
let height = this.htmlCanvas.clientHeight * window.devicePixelRatio;
if(inv[0] > 1){
const tl = vec2.transformMat3(vec2.create(), vec2.fromValues(0, 0), inv);
const br = vec2.transformMat3(vec2.create(), vec2.fromValues(width, height), inv);
x = tl[0];
y = tl[1];
width = br[0] - x;
height = br[1] - y;
}
this.context.fillRect(x, y, width, height);
this.context.fillRect(0, 0, width, height);
this.setTransform();
// Composite primitives
if (this.currentPrimitives == null) return;
for(var primitive of this.currentPrimitives){
for (var primitive of this.currentPrimitives) {
let opacity = this.GetLayerOpacity(primitive.layerId) * this.globalOpacity;
let filled = this.ShowLayerFilled(primitive.layerId) && this.globalFill;
if(!filled || opacity == 0.0){
if (!filled || opacity == 0.0) {
continue;
}
@ -750,14 +698,12 @@ export default class Canvas2D extends CanvasBase
}
}
StartPlaying()
{
StartPlaying() {
// pre-cache
for(let primitives of this.framePrimitives){
for(let primitive of primitives){
for (let primitives of this.framePrimitives) {
for (let primitive of primitives) {
let cacheId = primitive.GetObjectCacheId();
if(cacheId != null)
{
if (cacheId != null) {
this.objectCache.AddUser(cacheId);
}
}
@ -766,35 +712,31 @@ export default class Canvas2D extends CanvasBase
super.StartPlaying();
}
StopPlaying()
{
super.StopPlaying();
StopPlaying() {
super.StopPlaying();
// free up the cache
for(let primitives of this.framePrimitives){
for(let primitive of primitives){
for (let primitives of this.framePrimitives) {
for (let primitive of primitives) {
let imageId = primitive.GetObjectCacheId();
if(imageId != null)
{
if (imageId != null) {
this.objectCache.RemoveUser(imageId);
}
}
}
}
HandleKeyDown(key : string) : [boolean, boolean]
{
HandleKeyDown(key: string): [boolean, boolean] {
var result = super.HandleKeyDown(key);
if (result[0]) return result; // Already handled
let handled = true;
key = key.toLowerCase();
switch(key)
{
switch (key) {
case "r":
this.ResetView();
break;
default:
handled = false;
break;
@ -803,32 +745,72 @@ export default class Canvas2D extends CanvasBase
return [handled, false];
}
HandlePointerDown(point: vec2, event: PointerEvent) : void {
this.initFocusPoint = vec2.copy(vec2.create(), this.focusPoint);
super.HandlePointerDown(point, event);
pointerCenter(): vec2 {
if (this.pointerCoords.size == 1) {
return this.pointerCoords.get(this.pid0);
} else {
const p0 = this.pointerCoords.get(this.pid0);
const p1 = this.pointerCoords.get(this.pid1);
let center = vec2.add(vec2.create(), p0, p1);
vec2.scale(center, center, 0.5);
return center;
}
}
HandlePointerMove(point: vec2, event: PointerEvent): void {
updateFocusPointDelta(): void {
if (this.pointerCoords.size == 0) {
return
}
const center = this.pointerCenter();
const focusPoint = vec2.scale(vec2.create(), this.focusPoint, 1 / window.devicePixelRatio);
this.focusPointDelta = vec2.subtract(vec2.create(), focusPoint, center);
}
HandlePointerDown(point: vec2, event: PointerEvent): void {
super.HandlePointerDown(point, event);
this.updateFocusPointDelta();
this.updateTransform();
}
HandlePointerUp(event: PointerEvent): void {
super.HandlePointerUp(event);
this.updateFocusPointDelta();
this.updateTransform();
}
HandlePointerMoveWithTwist(point: vec2, twistAngle: number, event: PointerEvent): void {
const countPointers = this.pointerCoords.size;
if (countPointers == 0) return;
if (countPointers == 0 || countPointers > 2) return;
const init = this.initPointerCoords.get(event.pointerId);
const transform = this.getTransform();
const inv = mat3.invert(mat3.create(), transform);
if (countPointers == 2) {
const pinchZoom = this.PinchZoom(point, event);
this.scale = this.scale * pinchZoom.distanceRatio;
this.angle = this.angle + pinchZoom.angleDelta;
const source = vec2.transformMat3(vec2.create(), init, inv);
const dest = vec2.transformMat3(vec2.create(), point, inv);
const delta = vec2.subtract(vec2.create(), source, dest);
vec2.add(this.focusPoint, this.initFocusPoint, delta);
// Set focus to mean of two points
vec2.add(this.focusPoint, pinchZoom.center, this.focusPointDelta);
vec2.scale(this.focusPoint, this.focusPoint, window.devicePixelRatio);
} else {
this.angle += twistAngle;
if (twistAngle == 0) {
vec2.add(this.focusPoint, point, this.focusPointDelta);
vec2.scale(this.focusPoint, this.focusPoint, window.devicePixelRatio);
}
}
this.updateTransform();
this.setTransform();
super.HandlePointerMove(point, event);
}
HandleMouseWheel(event: WheelEvent): void {
const factor = Math.pow(1.1, event.deltaY > 0 ? -1 : 1);
this.scale *= factor;
this.updateTransform();
this.setTransform();
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,14 +1,12 @@
export default class DropDownMenu
{
title : string;
header : HTMLSpanElement;
wrapper : HTMLDivElement; // Contains both contents and inputBox
contents : HTMLSpanElement;
dropDownMenu : HTMLSelectElement;
export default class DropDownMenu {
title: string;
header: HTMLSpanElement;
wrapper: HTMLDivElement; // Contains both contents and inputBox
contents: HTMLSpanElement;
dropDownMenu: HTMLSelectElement;
constructor(title : string, style : string, parent : HTMLElement)
{
constructor(title: string, style: string, parent: HTMLElement) {
// Create div
var div = document.createElement("div");
div.className = "scenepic"; // CSS style class name
@ -40,10 +38,8 @@ export default class DropDownMenu
parent.appendChild(div);
}
SetItems(items : string[])
{
items.forEach(item =>
{
SetItems(items: string[]) {
items.forEach(item => {
// Create table row
var option = document.createElement("option");
option.value = item;
@ -54,24 +50,20 @@ export default class DropDownMenu
this.Update();
}
SetItemDisabled(index : number, disabled : boolean)
{
SetItemDisabled(index: number, disabled: boolean) {
this.dropDownMenu.options[index].disabled = disabled;
}
SetSelection(index : number)
{
SetSelection(index: number) {
this.dropDownMenu.selectedIndex = index;
}
SetTitle(htmlTitle : string)
{
SetTitle(htmlTitle: string) {
this.title = htmlTitle;
this.Update();
}
Update()
{
Update() {
this.header.style.display = (this.title == null || this.title == "") ? "none" : "";
this.header.innerHTML = this.title;
this.wrapper.style.display = "inline-block";

Просмотреть файл

@ -29,19 +29,19 @@ class Rect {
this.height = height;
}
get topEdge() : number {
get topEdge(): number {
return this.y;
}
get bottomEdge() : number {
get bottomEdge(): number {
return this.y + this.height;
}
get leftEdge() : number {
get leftEdge(): number {
return this.x;
}
get rightEdge() : number {
get rightEdge(): number {
return this.x + this.width;
}
@ -93,27 +93,27 @@ class Layout {
width: number;
height: number;
get topEdge() : number {
get topEdge(): number {
return this.y;
}
get bottomEdge() : number {
get bottomEdge(): number {
return this.y + this.height;
}
get leftEdge() : number {
get leftEdge(): number {
return this.x;
}
get rightEdge() : number {
get rightEdge(): number {
return this.x + this.width;
}
constructor({top = new Rect(),
right = new Rect(),
bottom = new Rect(),
left = new Rect(),
center = new Rect()} : {top? : Rect | Layout, right? : Rect | Layout, bottom? : Rect | Layout, left? : Rect | Layout, center? : Rect | Layout} = {}) {
constructor({ top = new Rect(),
right = new Rect(),
bottom = new Rect(),
left = new Rect(),
center = new Rect() }: { top?: Rect | Layout, right?: Rect | Layout, bottom?: Rect | Layout, left?: Rect | Layout, center?: Rect | Layout } = {}) {
this.x = this.y = this.width = this.height = 0;
this.top = top;
this.right = right;
@ -404,7 +404,7 @@ export default class Graph extends CanvasBase {
let layout = new Layout();
let nameRect = new Rect(0, 0, nameColumnWidth, nameHeight);
let nameLayout = new Layout({left: nameRect});
let nameLayout = new Layout({ left: nameRect });
switch (this.nameAlign) {
case "top":
layout.top = nameLayout;
@ -426,34 +426,34 @@ export default class Graph extends CanvasBase {
let valueRect = new Rect(0, 0, valueColumnWidth, valueHeight);
switch (this.valueAlign) {
case "top":
if(layout.top instanceof Layout){
if (layout.top instanceof Layout) {
layout.top.right = valueRect;
}else{
layout.top = new Layout({right: valueRect})
} else {
layout.top = new Layout({ right: valueRect })
}
break;
case "bottom":
if(layout.bottom instanceof Layout){
if (layout.bottom instanceof Layout) {
layout.bottom.right = valueRect;
}else{
layout.bottom = new Layout({right: valueRect})
} else {
layout.bottom = new Layout({ right: valueRect })
}
break;
case "left":
if(layout.left instanceof Layout){
if (layout.left instanceof Layout) {
layout.left.right = valueRect;
}else{
layout.left = new Layout({right: valueRect})
} else {
layout.left = new Layout({ right: valueRect })
}
break;
case "right":
if(layout.right instanceof Layout){
if (layout.right instanceof Layout) {
layout.right.right = valueRect;
}else{
layout.right = new Layout({right: valueRect})
} else {
layout.right = new Layout({ right: valueRect })
}
break;
}

Просмотреть файл

@ -1,53 +1,51 @@
// NB ONLY TO BE USED INTERNALLY, NOT IN API
import Misc from "./Misc"
import {vec3, vec4, mat4} from "gl-matrix";
import { vec3, vec4, mat4 } from "gl-matrix";
import { VertexBuffer, VertexBufferType } from "./VertexBuffers";
export default class Mesh
{
static readonly ElementsPerTriangle : number = 3;
static readonly ElementsPerLine : number = 2;
ElementsPerVertex : number;
VertexOffsetPosition : number = 0;
VertexOffsetNormal : number = 3;
VertexOffsetColor : number = 0; // Dummy value - simplifies logic in webglmeshbuffers
VertexOffsetTexture : number = 0; // Dummy value - simplifies logic in webglmeshbuffers
export default class Mesh {
static readonly ElementsPerTriangle: number = 3;
static readonly ElementsPerLine: number = 2;
ElementsPerVertex: number;
VertexOffsetPosition: number = 0;
VertexOffsetNormal: number = 3;
VertexOffsetColor: number = 0; // Dummy value - simplifies logic in webglmeshbuffers
VertexOffsetTexture: number = 0; // Dummy value - simplifies logic in webglmeshbuffers
ElementsPerInstance : number;
InstanceOffsetPosition : number = 0;
InstanceOffsetRotation : number = 0;
InstanceOffsetColor : number = 0;
ElementsPerInstance: number;
InstanceOffsetPosition: number = 0;
InstanceOffsetRotation: number = 0;
InstanceOffsetColor: number = 0;
// Host memory buffers
centerOfMass : vec3; // Mean of vertex positions, to allow depth ordering for improved transparency
vertexBuffer : Float32Array; // Vertex.Length elements per vertex
bytesPerIndex : number; // 2 or 4
triangleBuffer : ArrayBuffer; // 3 elements (v1, v2, v3) per triangle
lineBuffer : ArrayBuffer; // 2 elements (v1, v2) per line
wireframeEdgeBuffer : ArrayBuffer; // 2 elements (v1, v2) per edge, used for wireframe
instanceBuffer : Float32Array; // Used for instancing
instanceBufferHasRotations : boolean;
instanceBufferHasColors : boolean;
color : vec3; // Either null (per-vertex coloring stored in vertex buffer) or a 3-vector (shared color across whole mesh)
textureId : string;
isBillboard : boolean; // If true, zero out rotation
isLabel : boolean; // If true, treat the mesh specially as a label
nnTexture : boolean; // If true, render textures using nearest neighbor interpolation, otherwise linear
useTextureAlpha : boolean; // If true, use the texture's alpha channel for transparency. Otherwise treat as opaque.
cameraSpace : boolean; // If true, the mesh is defined in camera-space rather than world space and will thus not move with mouse commands
layerId : string; // Unique identifier for a layer of Meshes with shared properties
doubleSided : boolean; // If true, then back-face culling is turned off
centerOfMass: vec3; // Mean of vertex positions, to allow depth ordering for improved transparency
vertexBuffer: Float32Array; // Vertex.Length elements per vertex
bytesPerIndex: number; // 2 or 4
triangleBuffer: ArrayBuffer; // 3 elements (v1, v2, v3) per triangle
lineBuffer: ArrayBuffer; // 2 elements (v1, v2) per line
wireframeEdgeBuffer: ArrayBuffer; // 2 elements (v1, v2) per edge, used for wireframe
instanceBuffer: Float32Array; // Used for instancing
instanceBufferHasRotations: boolean;
instanceBufferHasColors: boolean;
color: vec3; // Either null (per-vertex coloring stored in vertex buffer) or a 3-vector (shared color across whole mesh)
textureId: string;
isBillboard: boolean; // If true, zero out rotation
isLabel: boolean; // If true, treat the mesh specially as a label
nnTexture: boolean; // If true, render textures using nearest neighbor interpolation, otherwise linear
useTextureAlpha: boolean; // If true, use the texture's alpha channel for transparency. Otherwise treat as opaque.
cameraSpace: boolean; // If true, the mesh is defined in camera-space rather than world space and will thus not move with mouse commands
layerId: string; // Unique identifier for a layer of Meshes with shared properties
doubleSided: boolean; // If true, then back-face culling is turned off
// Properties
CountVertices() : number { return this.vertexBuffer.length / this.ElementsPerVertex; }
CountTriangles() : number { return this.triangleBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerTriangle); }
CountLines() : number { return this.lineBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerLine); }
CountWireframeEdges() : number { return this.wireframeEdgeBuffer != null ? this.wireframeEdgeBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerLine) : 0; }
CountInstances() : number { return this.instanceBuffer != null ? (this.instanceBuffer.length / this.ElementsPerInstance) : 1; }
CountVertices(): number { return this.vertexBuffer.length / this.ElementsPerVertex; }
CountTriangles(): number { return this.triangleBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerTriangle); }
CountLines(): number { return this.lineBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerLine); }
CountWireframeEdges(): number { return this.wireframeEdgeBuffer != null ? this.wireframeEdgeBuffer.byteLength / (this.bytesPerIndex * Mesh.ElementsPerLine) : 0; }
CountInstances(): number { return this.instanceBuffer != null ? (this.instanceBuffer.length / this.ElementsPerInstance) : 1; }
// Constructor
constructor(vertexBuffer : Float32Array, bytesPerIndex : number, triangleBuffer : ArrayBuffer, lineBuffer : ArrayBuffer, color : vec3, textureId : string = null, nnTexture : boolean = true, useTextureAlpha = true, instanceBuffer : Float32Array = null, instanceBufferHasRotations = false, instanceBufferHasColors = false)
{
constructor(vertexBuffer: Float32Array, bytesPerIndex: number, triangleBuffer: ArrayBuffer, lineBuffer: ArrayBuffer, color: vec3, textureId: string = null, nnTexture: boolean = true, useTextureAlpha = true, instanceBuffer: Float32Array = null, instanceBufferHasRotations = false, instanceBufferHasColors = false) {
this.vertexBuffer = vertexBuffer;
this.bytesPerIndex = bytesPerIndex;
this.triangleBuffer = triangleBuffer;
@ -63,19 +61,17 @@ export default class Mesh
this.layerId = null;
// Deal with instance buffer
if (instanceBuffer == null)
{
if (instanceBuffer == null) {
this.instanceBuffer = null;
this.instanceBufferHasRotations = false;
this.instanceBufferHasColors = false;
}
else
{
else {
this.instanceBuffer = instanceBuffer;
this.instanceBufferHasRotations = instanceBufferHasRotations;
this.instanceBufferHasColors = instanceBufferHasColors;
}
// Compute number of elements per vertex
this.ElementsPerVertex = 6; // Position and normal: X,Y,Z,NX,NY,NZ
if (this.color == null) // I.e. per-vertex rather than per-mesh color
@ -83,31 +79,26 @@ export default class Mesh
this.VertexOffsetColor = this.ElementsPerVertex;
this.ElementsPerVertex += 3; // Per-vertex color: R,G,B
}
if (this.textureId != null)
{
if (this.textureId != null) {
this.VertexOffsetTexture = this.ElementsPerVertex;
this.ElementsPerVertex += 2; // Texture coords: U,V
}
// Compute number of elements per instance
this.ElementsPerInstance = 3; // Translation: X,Y,Z
if (this.instanceBufferHasRotations)
{
if (this.instanceBufferHasRotations) {
this.InstanceOffsetRotation = this.ElementsPerInstance;
this.ElementsPerInstance += 4; // Quaternion rotation: W,X,Y,Z
}
if (this.instanceBufferHasColors)
{
if (this.instanceBufferHasColors) {
this.InstanceOffsetColor = this.ElementsPerInstance;
this.ElementsPerInstance += 3; // Color: R,G,B
}
// Compute center of mass
this.centerOfMass = vec3.create();
if (this.CountVertices() > 0)
{
for(var i = 0; i < this.vertexBuffer.length; i += this.ElementsPerVertex)
{
if (this.CountVertices() > 0) {
for (var i = 0; i < this.vertexBuffer.length; i += this.ElementsPerVertex) {
this.centerOfMass[0] += this.vertexBuffer[i + 0];
this.centerOfMass[1] += this.vertexBuffer[i + 1];
this.centerOfMass[2] += this.vertexBuffer[i + 2];
@ -118,32 +109,28 @@ export default class Mesh
}
}
Update(buffer : Float32Array, update_flags: VertexBufferType) : Mesh
{
if(this.CountInstances() > 1)
{
Update(buffer: Float32Array, update_flags: VertexBufferType): Mesh {
if (this.CountInstances() > 1) {
return this.ToInstanceBuffer(buffer, update_flags);
}
else
{
else {
return this.ToVertexBuffer(buffer, update_flags);
}
}
ToInstanceBuffer(update : Float32Array, updateFlags: VertexBufferType) : Mesh
{
ToInstanceBuffer(update: Float32Array, updateFlags: VertexBufferType): Mesh {
let instanceBuffer = VertexBuffer.Update(this.instanceBuffer, this.ElementsPerInstance, update, updateFlags);
let mesh = new Mesh(this.vertexBuffer,
this.bytesPerIndex,
this.triangleBuffer,
this.lineBuffer,
this.color,
this.textureId,
this.nnTexture,
this.useTextureAlpha,
instanceBuffer,
this.instanceBufferHasRotations,
this.instanceBufferHasColors)
this.bytesPerIndex,
this.triangleBuffer,
this.lineBuffer,
this.color,
this.textureId,
this.nnTexture,
this.useTextureAlpha,
instanceBuffer,
this.instanceBufferHasRotations,
this.instanceBufferHasColors)
mesh.cameraSpace = this.cameraSpace;
mesh.layerId = this.layerId;
mesh.doubleSided = this.doubleSided;
@ -152,20 +139,19 @@ export default class Mesh
return mesh;
}
ToVertexBuffer(update : Float32Array, updateFlags: VertexBufferType) : Mesh
{
ToVertexBuffer(update: Float32Array, updateFlags: VertexBufferType): Mesh {
let vertexBuffer = VertexBuffer.Update(this.vertexBuffer, this.ElementsPerVertex, update, updateFlags);
let mesh = new Mesh(vertexBuffer,
this.bytesPerIndex,
this.triangleBuffer,
this.lineBuffer,
this.color,
this.textureId,
this.nnTexture,
this.useTextureAlpha,
this.instanceBuffer,
this.instanceBufferHasRotations,
this.instanceBufferHasColors)
this.bytesPerIndex,
this.triangleBuffer,
this.lineBuffer,
this.color,
this.textureId,
this.nnTexture,
this.useTextureAlpha,
this.instanceBuffer,
this.instanceBufferHasRotations,
this.instanceBufferHasColors)
mesh.cameraSpace = this.cameraSpace;
mesh.layerId = this.layerId;
mesh.doubleSided = this.doubleSided;
@ -174,15 +160,13 @@ export default class Mesh
return mesh;
}
static Parse(definition : any)
{
var color : vec3 = null;
var textureId : string = null;
var nnTexture : boolean = true;
var useTextureAlpha : boolean = true;
static Parse(definition: any) {
var color: vec3 = null;
var textureId: string = null;
var nnTexture: boolean = true;
var useTextureAlpha: boolean = true;
switch(definition["PrimitiveType"])
{
switch (definition["PrimitiveType"]) {
case "SingleColorMesh":
color = <vec3>Misc.Base64ToFloat32Array(definition["Color"]);
textureId = Misc.GetDefault(definition, "TextureId", null);
@ -191,9 +175,8 @@ export default class Mesh
case "MultiColorMesh":
let vertexBuffer = Misc.Base64ToFloat32Array(definition["VertexBuffer"]);
var indexBufferType = definition["IndexBufferType"];
var bytesPerIndex : number, triangleBuffer : ArrayBuffer, lineBuffer : ArrayBuffer;
if (indexBufferType == "UInt16")
{
var bytesPerIndex: number, triangleBuffer: ArrayBuffer, lineBuffer: ArrayBuffer;
if (indexBufferType == "UInt16") {
bytesPerIndex = 2;
triangleBuffer = Misc.Base64ToUInt16Array(definition["TriangleBuffer"]).buffer;
lineBuffer = Misc.Base64ToUInt16Array(definition["LineBuffer"]).buffer;
@ -209,20 +192,17 @@ export default class Mesh
var instanceBufferHasColors = Misc.GetDefault(definition, "InstanceBufferHasColors", false);
return new Mesh(vertexBuffer, bytesPerIndex, triangleBuffer, lineBuffer, color, textureId, nnTexture, useTextureAlpha, instanceBuffer, instanceBufferHasRotations, instanceBufferHasColors);
default:
throw "Unknown \"Primitive\" type in mesh: " + JSON.stringify(definition);
throw "Unknown \"Primitive\" type in mesh: " + JSON.stringify(definition);
}
}
GetWireframeEdgeBuffer()
{
if (this.wireframeEdgeBuffer == null)
{
GetWireframeEdgeBuffer() {
if (this.wireframeEdgeBuffer == null) {
this.wireframeEdgeBuffer = new ArrayBuffer(this.triangleBuffer.byteLength * 2);
var triangleView : any = this.bytesPerIndex == 2 ? new Uint16Array(this.triangleBuffer) : new Uint32Array(this.triangleBuffer);
var wireframeView : any = this.bytesPerIndex == 2 ? new Uint16Array(this.wireframeEdgeBuffer) : new Uint32Array(this.wireframeEdgeBuffer);
var triangleView: any = this.bytesPerIndex == 2 ? new Uint16Array(this.triangleBuffer) : new Uint32Array(this.triangleBuffer);
var wireframeView: any = this.bytesPerIndex == 2 ? new Uint16Array(this.wireframeEdgeBuffer) : new Uint32Array(this.wireframeEdgeBuffer);
for(var i = 0; i < triangleView.length; i += 3)
{
for (var i = 0; i < triangleView.length; i += 3) {
wireframeView[2 * i + 5] = wireframeView[2 * i + 0] = triangleView[i + 0];
wireframeView[2 * i + 2] = wireframeView[2 * i + 1] = triangleView[i + 1];
wireframeView[2 * i + 4] = wireframeView[2 * i + 3] = triangleView[i + 2];

Просмотреть файл

@ -4,11 +4,11 @@ import ShaderProgram from "./Shaders";
import WebGLMeshBuffers from "./WebGLMeshBuffers";
export class MeshPicker {
readonly frameBuffer : WebGLFramebuffer;
readonly program : ShaderProgram;
data : Uint8Array;
readonly frameBuffer: WebGLFramebuffer;
readonly program: ShaderProgram;
data: Uint8Array;
constructor(gl : WebGL2RenderingContext, width: number, height: number){
constructor(gl: WebGL2RenderingContext, width: number, height: number) {
this.program = new ShaderProgram(gl, "pickerVertex", "pickerFragment");
let targetTexture = Misc.AssertNotNull(gl.createTexture(), "WebGL returned a null texture");
@ -17,9 +17,9 @@ export class MeshPicker {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
width, height, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
width, height, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
let depthBuffer = Misc.AssertNotNull(gl.createRenderbuffer(), "WebGL returned a null render buffer");
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
@ -28,14 +28,14 @@ export class MeshPicker {
gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
this.data = new Uint8Array(4);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
Pick(gl : WebGL2RenderingContext, meshes: [WebGLMeshBuffers, mat4][], point: vec2, v2sMatrix: mat4) : number {
Pick(gl: WebGL2RenderingContext, meshes: [WebGLMeshBuffers, mat4][], point: vec2, v2sMatrix: mat4): number {
const canvas = <HTMLCanvasElement>gl.canvas;
gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer);
@ -50,7 +50,7 @@ export class MeshPicker {
const program = this.program;
gl.useProgram(program.program);
meshes.forEach(function([buffer, w2vMatrix]) {
meshes.forEach(function ([buffer, w2vMatrix]) {
buffer.RenderPicker(program, v2sMatrix, w2vMatrix)
});

Просмотреть файл

@ -1,60 +1,50 @@
import {mat3, mat4, vec3, vec4, quat} from "gl-matrix";
import { mat3, mat4, vec3, vec4, quat } from "gl-matrix";
import * as pako from "pako";
export default class Misc
{
static Sign(value: number): number
{
if(value < 0)
{
export default class Misc {
static Sign(value: number): number {
if (value < 0) {
return -1;
}
if(value > 0)
{
if (value > 0) {
return 1;
}
return 0;
}
static Mat3FromMat4(m4 : mat4) : mat3
{
static Mat3FromMat4(m4: mat4): mat3 {
var m3 = mat3.create();
mat3.fromMat4(m3, m4);
return m3;
}
static NormalizeVec3InPlace(vec : Float32Array, ind : number)
{
var norm = 1.0/Math.sqrt(vec[ind] * vec[ind] + vec[ind+1] * vec[ind+1] + vec[ind+2] * vec[ind+2]);
static NormalizeVec3InPlace(vec: Float32Array, ind: number) {
var norm = 1.0 / Math.sqrt(vec[ind] * vec[ind] + vec[ind + 1] * vec[ind + 1] + vec[ind + 2] * vec[ind + 2]);
vec[ind] *= norm;
vec[ind+1] *= norm;
vec[ind+2] *= norm;
vec[ind + 1] *= norm;
vec[ind + 2] *= norm;
}
static GetDefault(obj, name, defaultValue) : any
{
static GetDefault(obj, name, defaultValue): any {
if (name in obj)
return obj[name];
else
return defaultValue;
}
static PushOrCreateArray(obj, key, value)
{
static PushOrCreateArray(obj, key, value) {
if (!(key in obj))
obj[key] = [];
obj[key].push(value);
}
static DecoderArray : any = null;
static DecoderArray: any = null;
static Base64ToArrayBuffer(base64str : string)
{
static Base64ToArrayBuffer(base64str: string) {
// Initialize decoder array if necessary (not threadsafe)
if (Misc.DecoderArray == null)
{
if (Misc.DecoderArray == null) {
const CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
Misc.DecoderArray = {};
for (var i = 0; i < CODES.length; i++)
@ -70,8 +60,7 @@ export default class Misc
var aView = new Uint8Array(aBuff);
// Decode
for(var i = 0, j = 0; i < countBytes; i += 3)
{
for (var i = 0, j = 0; i < countBytes; i += 3) {
// Get characters
var c0 = base64str.charCodeAt(j++);
var c1 = base64str.charCodeAt(j++);
@ -85,11 +74,11 @@ export default class Misc
var e3 = Misc.DecoderArray[c3];
aView[i] = (e0 << 2) | (e1 >> 4);
if (e2 != 64) aView[i+1] = ((e1 & 15) << 4) | (e2 >> 2);
if (e3 != 64) aView[i+2] = ((e2 & 3) << 6) | e3;
if (e2 != 64) aView[i + 1] = ((e1 & 15) << 4) | (e2 >> 2);
if (e3 != 64) aView[i + 2] = ((e2 & 3) << 6) | e3;
}
let result : Uint8Array;
let result: Uint8Array;
try {
result = pako.inflate(aView);
} catch (err) {
@ -99,29 +88,25 @@ export default class Misc
return result.buffer;
}
static DataUrlToBlob(dataUrl: string) : Blob
{
static DataUrlToBlob(dataUrl: string): Blob {
let parts = dataUrl.split(',');
let mime = parts[0].match(/:(.*?);/)[1];
let array = new Uint8Array(Misc.Base64ToArrayBuffer(parts[1]))
return new Blob([array], {"type": mime});
return new Blob([array], { "type": mime });
}
static Base64ToBlob(base64: string, mime: string) : Blob
{
static Base64ToBlob(base64: string, mime: string): Blob {
let array = new Uint8Array(Misc.Base64ToArrayBuffer(base64));
return new Blob([array], {"type": mime});
return new Blob([array], { "type": mime });
}
static DataUrlToArrayBuffer(dataUrl: string) : ArrayBuffer
{
static DataUrlToArrayBuffer(dataUrl: string): ArrayBuffer {
let parts = dataUrl.split(',');
return Misc.Base64ToArrayBuffer(parts[1]);
}
// Convert either from Base64 string or from regular array to Float32Array
static Base64ToFloat32Array(obj : any)
{
static Base64ToFloat32Array(obj: any) {
if (obj == null) return null;
if (typeof obj == "string")
return new Float32Array(Misc.Base64ToArrayBuffer(obj));
@ -129,8 +114,7 @@ export default class Misc
return new Float32Array(obj);
}
static Base64ToUInt8Array(obj: any)
{
static Base64ToUInt8Array(obj: any) {
if (obj == null) return null;
if (typeof obj == "string")
return new Uint8Array(Misc.Base64ToArrayBuffer(obj));
@ -139,8 +123,7 @@ export default class Misc
}
// Convert either from Base64 string or from regular array to Int16Array
static Base64ToUInt16Array(obj : any)
{
static Base64ToUInt16Array(obj: any) {
if (obj == null) return null;
if (typeof obj == "string")
return new Uint16Array(Misc.Base64ToArrayBuffer(obj));
@ -149,8 +132,7 @@ export default class Misc
}
// Convert either from Base64 string or from regular array to Int32Array
static Base64ToUInt32Array(obj : any)
{
static Base64ToUInt32Array(obj: any) {
if (obj == null) return null;
if (typeof obj == "string")
return new Uint32Array(Misc.Base64ToArrayBuffer(obj));
@ -158,24 +140,20 @@ export default class Misc
return new Uint32Array(obj);
}
static GetSearchValue(name : string)
{
static GetSearchValue(name: string) {
var searchStr = location.search.substring(1);
var vars = searchStr.split('&');
for (var i = 0; i < vars.length; i++)
{
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (decodeURIComponent(pair[0]) == name)
{
if (decodeURIComponent(pair[0]) == name) {
return decodeURIComponent(pair[1]);
}
}
return null;
}
static StyleToHtmlHex(style: Uint8Array) : string | null
{
if(style[0] == 1){
static StyleToHtmlHex(style: Uint8Array): string | null {
if (style[0] == 1) {
return null;
}
@ -185,24 +163,22 @@ export default class Misc
return "#" + Misc.Byte2Hex(red) + Misc.Byte2Hex(green) + Misc.Byte2Hex(blue);
}
static Byte2Hex (value: number) : string
{
const hex_string = "0123456789ABCDEF";
return String(hex_string.substr((value >> 4) & 0x0F,1)) + hex_string.substr(value & 0x0F,1);
static Byte2Hex(value: number): string {
const hex_string = "0123456789ABCDEF";
return String(hex_string.substr((value >> 4) & 0x0F, 1)) + hex_string.substr(value & 0x0F, 1);
}
static IsPow2(val : number)
{
static IsPow2(val: number) {
var l = Math.log(val) / Math.log(2);
return l == Math.floor(l);
}
static AssertNotNull<T>(value: T | null, error: string) : T {
if(value == null){
static AssertNotNull<T>(value: T | null, error: string): T {
if (value == null) {
throw new Error(error)
}
return value
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,18 +1,18 @@
import SPScene from "./SPScene"
function scenepic(id: string, commands: any, wait=100){
let element : HTMLElement | null = null;
if(id != null){
function scenepic(id: string, commands: any, wait = 100) {
let element: HTMLElement | null = null;
if (id != null) {
element = document.getElementById(id);
if(element == null){
if (element == null) {
console.warn(id + " does not exist in the DOM, retrying");
setTimeout(() => { scenepic(id, commands, wait * 2); }, wait);
}else{
} else {
let scene = new SPScene(element);
scene.ExecuteSceneCommands(commands);
}
}else{
} else {
let scene = new SPScene();
scene.ExecuteSceneCommands(commands);
}

Просмотреть файл

@ -193,7 +193,7 @@ void main()
"pickerVertex": {
type: "x-shader/x-vertex",
script:
`#version 300 es
`#version 300 es
// Vertex inputs
in vec3 vertexPositionIn;
@ -224,8 +224,8 @@ void main()
`},
"pickerFragment": {
type: "x-shader/x-fragment",
script:
`#version 300 es
script:
`#version 300 es
precision mediump float;
uniform int u_id;

Просмотреть файл

@ -1,15 +1,13 @@
export default class TextPanel
{
minimized : boolean;
title : string;
header : HTMLSpanElement;
wrapper : HTMLDivElement; // Contains both contents and inputBox
contents : HTMLSpanElement;
inputBox : HTMLTextAreaElement;
export default class TextPanel {
minimized: boolean;
title: string;
header: HTMLSpanElement;
wrapper: HTMLDivElement; // Contains both contents and inputBox
contents: HTMLSpanElement;
inputBox: HTMLTextAreaElement;
constructor(id : string, title : string, style : string, parent : HTMLElement, startMinimized = false, addInputBox = false)
{
constructor(id: string, title: string, style: string, parent: HTMLElement, startMinimized = false, addInputBox = false) {
// Create div
var div = document.createElement("div");
div.className = "scenepic"; // CSS style class name
@ -50,30 +48,26 @@ export default class TextPanel
parent.appendChild(div);
}
SetContents(htmlContents : string, append = false)
{
SetContents(htmlContents: string, append = false) {
if (append)
this.contents.innerHTML += htmlContents;
else
this.contents.innerHTML = htmlContents;
}
SetTitle(htmlTitle : string)
{
SetTitle(htmlTitle: string) {
this.title = htmlTitle;
this.Update();
}
Update()
{
Update() {
this.header.style.display = (this.title == null || this.title == "") ? "none" : "";
this.header.innerHTML = this.title + (this.minimized ? " &#9661;" : " &#9655;");
var newDisplayValue = this.minimized ? "none" : "inline-block";
this.wrapper.style.display = newDisplayValue;
}
SetInputBoxContents(htmlContents : string)
{
SetInputBoxContents(htmlContents: string) {
this.inputBox.value = htmlContents;
}
}

Просмотреть файл

@ -1,81 +1,66 @@
export const enum VertexBufferType
{
export const enum VertexBufferType {
None = 0,
Positions = 1,
Normals = 2,
Colors = 4,
Rotations = 8
Rotations = 8
};
export class VertexBuffer {
private static partialCopy(buffer: Float32Array, bufferStride: number,
update: Float32Array, updateOffset: number, updateSize: number) : Float32Array
{
update: Float32Array, updateOffset: number, updateSize: number): Float32Array {
buffer = Float32Array.from(buffer);
for(let b_i=0, u_i=0; b_i < buffer.length; b_i += bufferStride, u_i += updateSize)
{
for (let b_i = 0, u_i = 0; b_i < buffer.length; b_i += bufferStride, u_i += updateSize) {
buffer.set(update.slice(u_i, u_i + updateSize), b_i + updateOffset);
}
return buffer;
}
private static copyPosition(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
{
if(elementsPerRow == 3)
{
private static copyPosition(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
if (elementsPerRow == 3) {
return update;
}
return this.partialCopy(buffer, elementsPerRow, update, 0, 3);
}
private static copyNormal(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
{
private static copyNormal(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
return this.partialCopy(buffer, elementsPerRow, update, 3, 3);
}
private static copyRotation(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
{
private static copyRotation(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
return this.partialCopy(buffer, elementsPerRow, update, 3, 4);
}
private static copyColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
{
return this.partialCopy(buffer, elementsPerRow, update, elementsPerRow - 3, 3);
private static copyColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
return this.partialCopy(buffer, elementsPerRow, update, elementsPerRow - 3, 3);
}
private static copyPositionRotation(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
{
if(elementsPerRow == 7)
{
private static copyPositionRotation(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
if (elementsPerRow == 7) {
return update;
}
return this.partialCopy(buffer, elementsPerRow, update, 0, 7);
}
private static copyPositionNormal(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
{
if(elementsPerRow == 6)
{
private static copyPositionNormal(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
if (elementsPerRow == 6) {
return update;
}
return this.partialCopy(buffer, elementsPerRow, update, 0, 6);
}
private static copyPositionColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
{
if(elementsPerRow == 6)
{
private static copyPositionColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
if (elementsPerRow == 6) {
return update;
}
let offset = elementsPerRow - 3;
buffer = Float32Array.from(buffer);
for(let b_i=0, u_i=0; b_i < buffer.length; b_i += elementsPerRow, u_i += 6)
{
for (let b_i = 0, u_i = 0; b_i < buffer.length; b_i += elementsPerRow, u_i += 6) {
buffer.set(update.slice(u_i, u_i + 3), b_i);
buffer.set(update.slice(u_i + 3, u_i + 6), b_i + offset);
}
@ -83,50 +68,46 @@ export class VertexBuffer {
return buffer;
}
private static copyRotationColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
{
private static copyRotationColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
return this.partialCopy(buffer, elementsPerRow, update, 3, 7);
}
private static copyNormalColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number) : Float32Array
{
private static copyNormalColor(buffer: Float32Array, update: Float32Array, elementsPerRow: number): Float32Array {
return this.partialCopy(buffer, elementsPerRow, update, 3, 6);
}
static Update(buffer: Float32Array, elementsPerRow: number, update: Float32Array, updateFlags: VertexBufferType) : Float32Array
{
switch(updateFlags)
{
static Update(buffer: Float32Array, elementsPerRow: number, update: Float32Array, updateFlags: VertexBufferType): Float32Array {
switch (updateFlags) {
case VertexBufferType.Positions:
return this.copyPosition(buffer, update, elementsPerRow);
case VertexBufferType.Normals:
return this.copyNormal(buffer, update, elementsPerRow);
case VertexBufferType.Rotations:
return this.copyRotation(buffer, update, elementsPerRow);
case VertexBufferType.Colors:
return this.copyColor(buffer, update, elementsPerRow);
case VertexBufferType.Positions | VertexBufferType.Rotations:
return this.copyPositionRotation(buffer, update, elementsPerRow);
case VertexBufferType.Positions | VertexBufferType.Normals:
return this.copyPositionNormal(buffer, update, elementsPerRow);
case VertexBufferType.Positions | VertexBufferType.Colors:
return this.copyPositionColor(buffer, update, elementsPerRow);
case VertexBufferType.Rotations | VertexBufferType.Colors:
return this.copyRotationColor(buffer, update, elementsPerRow);
case VertexBufferType.Normals | VertexBufferType.Colors:
return this.copyNormalColor(buffer, update, elementsPerRow);
case VertexBufferType.Positions | VertexBufferType.Normals | VertexBufferType.Colors:
return update;
case VertexBufferType.Positions | VertexBufferType.Rotations | VertexBufferType.Colors:
return update;
}

Просмотреть файл

@ -1,47 +1,45 @@
import {quat, vec3, mat3, mat4} from "gl-matrix";
import { quat, vec3, mat3, mat4 } from "gl-matrix";
import Mesh from "./Mesh";
import Misc from "./Misc"
import ShaderProgram from "./Shaders";
export default class WebGLMeshBuffers
{
gl : WebGL2RenderingContext;
sp : ShaderProgram;
m : Mesh;
export default class WebGLMeshBuffers {
gl: WebGL2RenderingContext;
sp: ShaderProgram;
m: Mesh;
color : vec3;
color: vec3;
vertexDataBuffer : WebGLBuffer;
triangleIndexBuffer : WebGLBuffer;
lineIndexBuffer : WebGLBuffer;
wireframeEdgeIndexBuffer : WebGLBuffer;
instanceDataBuffer : WebGLBuffer;
vertexDataBuffer: WebGLBuffer;
triangleIndexBuffer: WebGLBuffer;
lineIndexBuffer: WebGLBuffer;
wireframeEdgeIndexBuffer: WebGLBuffer;
instanceDataBuffer: WebGLBuffer;
hasTexture : boolean;
texture : WebGLTexture;
indexType : number;
hasTexture: boolean;
texture: WebGLTexture;
m2wMatrix : mat4;
indexType: number;
id : number;
m2wMatrix: mat4;
id: number;
// Used for label computations
labelWidthNormalized : number = -1.0;
labelHeightNormalized : number = -1.0;
labelTranslateScreenX : number = 0.0;
labelTranslateScreenY : number = 0.0;
labelTranslateWorldX : number = 0.0;
labelTranslateWorldY : number = 0.0;
labelWidthNormalized: number = -1.0;
labelHeightNormalized: number = -1.0;
labelTranslateScreenX: number = 0.0;
labelTranslateScreenY: number = 0.0;
labelTranslateWorldX: number = 0.0;
labelTranslateWorldY: number = 0.0;
constructor(gl : WebGL2RenderingContext, sp : ShaderProgram, m : Mesh, textureSrc = null)
{
constructor(gl: WebGL2RenderingContext, sp: ShaderProgram, m: Mesh, textureSrc = null) {
this.gl = gl;
this.sp = sp;
this.m = m;
this.color = m.color;
this.indexType = m.bytesPerIndex == 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT;
// Create and bind vertex buffer
@ -85,18 +83,16 @@ export default class WebGLMeshBuffers
else
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([1, 1, 1, 1])); // Dummy texture
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, m.nnTexture ? gl.NEAREST : gl.LINEAR);
if (textureSrc != null && (Misc.IsPow2(textureSrc.width) && Misc.IsPow2(textureSrc.height)))
{
if (textureSrc != null && (Misc.IsPow2(textureSrc.width) && Misc.IsPow2(textureSrc.height))) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, m.nnTexture ? gl.NEAREST : gl.LINEAR_MIPMAP_NEAREST)
}
else
{
else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, m.nnTexture ? gl.NEAREST : gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, m.nnTexture ? gl.NEAREST : gl.LINEAR);
}
gl.bindTexture(gl.TEXTURE_2D, null);
@ -104,8 +100,7 @@ export default class WebGLMeshBuffers
this.m2wMatrix = mat4.create();
}
Finalize()
{
Finalize() {
var gl = this.gl;
gl.deleteBuffer(this.vertexDataBuffer);
gl.deleteBuffer(this.triangleIndexBuffer);
@ -117,8 +112,7 @@ export default class WebGLMeshBuffers
gl.deleteTexture(this.texture);
}
ApplyBillboardEffect(m2vMatrix : mat4)
{
ApplyBillboardEffect(m2vMatrix: mat4) {
// Get rotational matrix component of m2vMatrix
var det = mat4.determinant(m2vMatrix);
var scale = det != 0.0 ? 1.0 / Math.pow(det, 1.0 / 3.0) : 1.0;
@ -132,12 +126,11 @@ export default class WebGLMeshBuffers
// Invert and transform
quat.invert(q, q);
var transform = mat4.create();
mat4.fromRotationTranslationScaleOrigin(transform, q, [0,0,0], [1,1,1], this.m.centerOfMass);
mat4.fromRotationTranslationScaleOrigin(transform, q, [0, 0, 0], [1, 1, 1], this.m.centerOfMass);
mat4.multiply(m2vMatrix, m2vMatrix, transform);
}
ApplyLabel(m2vMatrix : mat4, v2sMatrix : mat4)
{
ApplyLabel(m2vMatrix: mat4, v2sMatrix: mat4) {
this.ApplyBillboardEffect(m2vMatrix);
// Helper function - careful - this references, not copies the array
@ -163,23 +156,21 @@ export default class WebGLMeshBuffers
// Transform with alignment translation and scale factor
var translate = vec3.fromValues(this.labelTranslateScreenX * labelDepth / v2sMatrix[0] + this.labelTranslateWorldX,
this.labelTranslateScreenY * labelDepth / v2sMatrix[5] + this.labelTranslateWorldY,
0.0);
this.labelTranslateScreenY * labelDepth / v2sMatrix[5] + this.labelTranslateWorldY,
0.0);
var scale = vec3.fromValues(scaleFactorX, scaleFactorY, 1.0);
var transform = mat4.create();
mat4.fromRotationTranslationScale(transform, quat.create(), translate, scale);
mat4.mul(m2vMatrix, m2vMatrix, transform);
}
RenderBuffer(v2sMatrix : mat4, w2vMatrix : mat4, opacity : number, renderFilled : boolean, renderWireframe : boolean)
{
RenderBuffer(v2sMatrix: mat4, w2vMatrix: mat4, opacity: number, renderFilled: boolean, renderWireframe: boolean) {
const gl = this.gl;
const sp = this.sp;
const m = this.m;
// Ensure we have wireframe buffer if needed
if (renderWireframe && this.wireframeEdgeIndexBuffer == null)
{
if (renderWireframe && this.wireframeEdgeIndexBuffer == null) {
this.wireframeEdgeIndexBuffer = gl.createBuffer();
var cpuBuffer = m.GetWireframeEdgeBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.wireframeEdgeIndexBuffer);
@ -199,7 +190,7 @@ export default class WebGLMeshBuffers
// Set model view matrix (i.e. concatenation of m2w, w2v)
gl.uniformMatrix4fv(sp.m2vMatrixPtr, false, m2vMatrix);
// Compute normals transformation matrix
var normMatrix = Misc.Mat3FromMat4(m2vMatrix);
mat3.invert(normMatrix, normMatrix);
@ -213,71 +204,59 @@ export default class WebGLMeshBuffers
// Bind vertex buffers
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexDataBuffer);
if (true)
{
if (true) {
gl.enableVertexAttribArray(sp.vertexPositionAttribLoc);
gl.vertexAttribPointer(sp.vertexPositionAttribLoc, 3, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetPosition * 4);
}
if (true)
{
if (true) {
gl.enableVertexAttribArray(sp.vertexNormalAttribLoc);
gl.vertexAttribPointer(sp.vertexNormalAttribLoc, 3, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetNormal * 4);
}
if (m.VertexOffsetColor != 0)
{
if (m.VertexOffsetColor != 0) {
gl.enableVertexAttribArray(sp.vertexColorAttribLoc);
gl.vertexAttribPointer(sp.vertexColorAttribLoc, 3, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetColor * 4);
}
else
{
else {
gl.disableVertexAttribArray(sp.vertexColorAttribLoc);
}
if (m.VertexOffsetTexture != 0)
{
if (m.VertexOffsetTexture != 0) {
gl.enableVertexAttribArray(sp.vertexTextureAttribLoc);
gl.vertexAttribPointer(sp.vertexTextureAttribLoc, 2, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetTexture * 4); // VertexOffsetTexture = 0 when no texture used
}
else
{
else {
gl.disableVertexAttribArray(sp.vertexTextureAttribLoc);
}
// Bind instance buffers
gl.bindBuffer(gl.ARRAY_BUFFER, this.instanceDataBuffer);
if (m.instanceBuffer != null)
{
if (m.instanceBuffer != null) {
gl.enableVertexAttribArray(sp.instancePositionAttribLoc);
gl.vertexAttribPointer(sp.instancePositionAttribLoc, 3, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetPosition * 4);
gl.vertexAttribDivisor(sp.instancePositionAttribLoc, 1);
}
else
{
else {
gl.disableVertexAttribArray(sp.instancePositionAttribLoc);
gl.vertexAttribDivisor(sp.instancePositionAttribLoc, 0);
}
if (m.instanceBufferHasRotations)
{
if (m.instanceBufferHasRotations) {
gl.enableVertexAttribArray(sp.instanceRotationAttribLoc);
gl.vertexAttribPointer(sp.instanceRotationAttribLoc, 4, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetRotation * 4);
gl.vertexAttribDivisor(sp.instanceRotationAttribLoc, 1);
}
else
{
else {
gl.disableVertexAttribArray(sp.instanceRotationAttribLoc);
gl.vertexAttribDivisor(sp.instanceRotationAttribLoc, 0);
}
if (m.instanceBufferHasColors)
{
if (m.instanceBufferHasColors) {
gl.enableVertexAttribArray(sp.instanceColorAttribLoc);
gl.vertexAttribPointer(sp.instanceColorAttribLoc, 3, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetColor * 4);
gl.vertexAttribDivisor(sp.instanceColorAttribLoc, 1);
}
else
{
else {
gl.disableVertexAttribArray(sp.instanceColorAttribLoc);
gl.vertexAttribDivisor(sp.instanceColorAttribLoc, 0);
}
// Set use instance rotation uniform1i
gl.uniform1i(sp.useInstanceRotation, m.instanceBufferHasRotations ? 1 : 0);
@ -293,8 +272,7 @@ export default class WebGLMeshBuffers
gl.uniform1f(sp.alphaPtr, opacity);
// Draw triangles
if (renderFilled)
{
if (renderFilled) {
gl.uniform1i(sp.shadingTypePtr, this.m.isLabel ? 2 : (this.hasTexture ? 1 : 0));
gl.uniform1f(sp.lightingMultiplierPtr, 1.0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.triangleIndexBuffer);
@ -302,8 +280,7 @@ export default class WebGLMeshBuffers
}
// Draw wireframe edges
if (renderWireframe)
{
if (renderWireframe) {
gl.uniform1i(sp.shadingTypePtr, -1);
gl.uniform1f(sp.lightingMultiplierPtr, opacity == 1.0 ? 0.4 : 0.5);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.wireframeEdgeIndexBuffer);
@ -322,8 +299,7 @@ export default class WebGLMeshBuffers
gl.bindTexture(gl.TEXTURE_2D, null);
}
RenderPicker(program: ShaderProgram, v2sMatrix : mat4, w2vMatrix : mat4)
{
RenderPicker(program: ShaderProgram, v2sMatrix: mat4, w2vMatrix: mat4) {
const gl = this.gl;
const sp = program;
const m = this.m;
@ -344,40 +320,35 @@ export default class WebGLMeshBuffers
// Set model view matrix (i.e. concatenation of m2w, w2v)
gl.uniformMatrix4fv(sp.m2vMatrixPtr, false, m2vMatrix);
// Bind vertex buffers
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexDataBuffer);
if (true)
{
if (true) {
gl.enableVertexAttribArray(sp.vertexPositionAttribLoc);
gl.vertexAttribPointer(sp.vertexPositionAttribLoc, 3, gl.FLOAT, false, m.ElementsPerVertex * 4, m.VertexOffsetPosition * 4);
}
// Bind instance buffers
gl.bindBuffer(gl.ARRAY_BUFFER, this.instanceDataBuffer);
if (m.instanceBuffer != null)
{
if (m.instanceBuffer != null) {
gl.enableVertexAttribArray(sp.instancePositionAttribLoc);
gl.vertexAttribPointer(sp.instancePositionAttribLoc, 3, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetPosition * 4);
gl.vertexAttribDivisor(sp.instancePositionAttribLoc, 1);
}
else
{
else {
gl.disableVertexAttribArray(sp.instancePositionAttribLoc);
gl.vertexAttribDivisor(sp.instancePositionAttribLoc, 0);
}
if (m.instanceBufferHasRotations)
{
if (m.instanceBufferHasRotations) {
gl.enableVertexAttribArray(sp.instanceRotationAttribLoc);
gl.vertexAttribPointer(sp.instanceRotationAttribLoc, 4, gl.FLOAT, false, m.ElementsPerInstance * 4, m.InstanceOffsetRotation * 4);
gl.vertexAttribDivisor(sp.instanceRotationAttribLoc, 1);
}
else
{
else {
gl.disableVertexAttribArray(sp.instanceRotationAttribLoc);
gl.vertexAttribDivisor(sp.instanceRotationAttribLoc, 0);
}
// Set use instance rotation uniform1i
gl.uniform1i(sp.useInstanceRotation, m.instanceBufferHasRotations ? 1 : 0);

Просмотреть файл

@ -1,4 +1,4 @@
"""Script to create the holodeck assets."""
"""Script to create the embedded TypeScript assets."""
import scenepic as sp