Skip to content
This repository has been archived by the owner on Jun 10, 2022. It is now read-only.

Commit

Permalink
Add rigid body ownership (#536)
Browse files Browse the repository at this point in the history
  • Loading branch information
nik0la31 committed May 20, 2020
1 parent 3d15c07 commit c2809a2
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 0 deletions.
6 changes: 6 additions & 0 deletions packages/functional-tests/src/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import LookAtTest from './look-at-test';
import PhysicsBounceTest from './physics-bounce-test';
import PhysicsFrictionTest from './physics-friction-test';
import PhysicsSimTest from './physics-sim-test';
import PhysicsCollisionTest from './physics-collision'
import PhysichFreeFallTest from './physics-free-fall'
import PhysicsHeadCollisionTest from './physics-head-collision'
import PrimitivesTest from './primitives-test';
import PromptTest from './prompt-test';
import ReparentTest from './reparent-test';
Expand Down Expand Up @@ -79,6 +82,9 @@ export const Factories = {
'physics-bounce': (...args) => new PhysicsBounceTest(...args),
'physics-friction': (...args) => new PhysicsFrictionTest(...args),
'physics-sim': (...args) => new PhysicsSimTest(...args),
'physics-free-fall': (...args) => new PhysichFreeFallTest(...args),
'physics-collision': (...args) => new PhysicsCollisionTest(...args),
'physics-head-collision': (...args) => new PhysicsHeadCollisionTest(...args),
'primitives': (...args) => new PrimitivesTest(...args),
'prompt': (...args) => new PromptTest(...args),
'reparent': (...args) => new ReparentTest(...args),
Expand Down
100 changes: 100 additions & 0 deletions packages/functional-tests/src/tests/physics-collision.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@

/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import * as MRE from '@microsoft/mixed-reality-extension-sdk';

import { Test } from '../test';
export default class PhysicsCollisionTest extends Test {

private assets: MRE.AssetContainer;

private b0: MRE.Actor;
private b1: MRE.Actor;

private redMat: MRE.Material;
private blueMat: MRE.Material;

public async run(root: MRE.Actor): Promise<boolean> {
this.assets = new MRE.AssetContainer(this.app.context);

this.redMat = this.assets.createMaterial('redBall', {
color: MRE.Color3.Red()
});
this.blueMat = this.assets.createMaterial('blueBall', {
color: MRE.Color3.Blue()
});

if (this.app.context.users.length > 0)
{
const userId = this.app.context.users[0].id;
this.createLabel(root, "RED", MRE.Color3.Red(), userId);
this.spawnBall(root, -1.5, 0.2, this.redMat, userId, { x: 1000, y: 0, z: 0});
}

if (this.app.context.users.length > 1)
{
const userId = this.app.context.users[1].id;
this.createLabel(root, "BLUE", MRE.Color3.Blue(), userId);
this.spawnBall(root, +1.0, 0.2, this.blueMat, userId, { x: 0, y: 0, z: 0});
}

await this.stoppedAsync();
return true;
}

private createLabel(root: MRE.Actor, text: string, col: MRE.Color3, userId: MRE.Guid) {

const label = MRE.Actor.Create(this.app.context, {
actor: {
name: 'label',
parentId: root.id,
exclusiveToUser: userId,
transform: { local: { position: { y: 1.5 } } },
text: {
contents: text,
height: 0.1,
anchor: MRE.TextAnchorLocation.TopCenter,
color: col
}
}
});
}

private spawnBall(root: MRE.Actor, width: number, height: number, mat: MRE.Material, userId: MRE.Guid,
force: Partial<MRE.Vector3Like>, ballRadius = 0.2, killTimeout = 5000) {
const ball = MRE.Actor.Create(this.app.context, {
actor: {
owner: userId,
parentId: root.id,
name: "ball",
appearance: {
meshId: this.assets.createSphereMesh('ball', ballRadius).id,
materialId: mat.id
},
transform: {
local: { position: { x: -width / 2 + width, y: height, z: -0.1 } }
},
rigidBody: {
mass: 3,
},
collider: { geometry: { shape: MRE.ColliderType.Auto } }
}
});

setTimeout(() => {
ball.rigidBody.addForce( force )
}, 1000);

setTimeout(() => {
// We need to disable rendering and move the ball before destroying it so that if it is currently
// colliding with a peg, it will exit collision first for the ref counting to work properly. Then
// we can destroy it after another second to process the move on the client.
ball.appearance.enabled = false;
ball.transform.app.position = new MRE.Vector3(0, -10, 0);
setTimeout(() => ball.destroy(), 1000);
}, killTimeout);
}
}
91 changes: 91 additions & 0 deletions packages/functional-tests/src/tests/physics-free-fall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import * as MRE from '@microsoft/mixed-reality-extension-sdk';

import { Test } from '../test';
export default class PhysicsFreeFallTest extends Test {

private assets: MRE.AssetContainer;
private redMat: MRE.Material;
private blueMat: MRE.Material;

public async run(root: MRE.Actor): Promise<boolean> {
this.assets = new MRE.AssetContainer(this.app.context);

this.redMat = this.assets.createMaterial('redBall', {
color: MRE.Color3.Red()
});
this.blueMat = this.assets.createMaterial('blueBall', {
color: MRE.Color3.Blue()
});

if (this.app.context.users.length > 0)
{
const userId = this.app.context.users[0].id;
this.createLabel(root, "RED", MRE.Color3.Red(), userId);
this.spawnBall(root, -0.5, 3.0, this.redMat, userId);
}

if (this.app.context.users.length > 1)
{
const userId = this.app.context.users[1].id;
this.createLabel(root, "BLUE", MRE.Color3.Blue(), userId);
this.spawnBall(root, +0.5, 3.0, this.blueMat, userId);
}

await this.stoppedAsync();
return true;
}

private createLabel(root: MRE.Actor, text: string, col: MRE.Color3, userId: MRE.Guid) {

const label = MRE.Actor.Create(this.app.context, {
actor: {
name: 'label',
parentId: root.id,
exclusiveToUser: userId,
transform: { local: { position: { y: 1.5 } } },
text: {
contents: text,
height: 0.1,
anchor: MRE.TextAnchorLocation.TopCenter,
color: col
}
}
});
}

private spawnBall(root: MRE.Actor, width: number, height: number, mat: MRE.Material, userId: MRE.Guid,
ballRadius = 0.07, killTimeout = 5000) {
const ball = MRE.Actor.Create(this.app.context, {
actor: {
owner: userId,
parentId: root.id,
name: "ball",
appearance: {
meshId: this.assets.createSphereMesh('ball', ballRadius).id,
materialId: mat.id
},
transform: {
local: { position: { x: -width / 2 + width, y: height, z: -0.1 } }
},
rigidBody: {
mass: 3,
},
collider: { geometry: { shape: MRE.ColliderType.Auto } }
}
});

setTimeout(() => {
// We need to disable rendering and move the ball before destroying it so that if it is currently
// colliding with a peg, it will exit collision first for the ref counting to work properly. Then
// we can destroy it after another second to process the move on the client.
ball.appearance.enabled = false;
ball.transform.app.position = new MRE.Vector3(0, -10, 0);
setTimeout(() => ball.destroy(), 1000);
}, killTimeout);
}
}
101 changes: 101 additions & 0 deletions packages/functional-tests/src/tests/physics-head-collision.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@

/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import * as MRE from '@microsoft/mixed-reality-extension-sdk';

import { Test } from '../test';
import { Vector3Like } from '@microsoft/mixed-reality-extension-sdk';
export default class PhysicsHeadCollisionTest extends Test {

private assets: MRE.AssetContainer;

private b0: MRE.Actor;
private b1: MRE.Actor;

private redMat: MRE.Material;
private blueMat: MRE.Material;

public async run(root: MRE.Actor): Promise<boolean> {
this.assets = new MRE.AssetContainer(this.app.context);

this.redMat = this.assets.createMaterial('redBall', {
color: MRE.Color3.Red()
});
this.blueMat = this.assets.createMaterial('blueBall', {
color: MRE.Color3.Blue()
});

if (this.app.context.users.length > 0)
{
const userId = this.app.context.users[0].id;
this.createLabel(root, "RED", MRE.Color3.Red(), userId);
this.spawnBall(root, -1.5, 0.2, this.redMat, userId, { x: 1000, y: 0, z: 0});
}

if (this.app.context.users.length > 1)
{
const userId = this.app.context.users[1].id;
this.createLabel(root, "BLUE", MRE.Color3.Blue(), userId);
this.spawnBall(root, +1.5, 0.2, this.blueMat, userId, { x: -1000, y: 0, z: 0});
}

await this.stoppedAsync();
return true;
}

private createLabel(root: MRE.Actor, text: string, col: MRE.Color3, userId: MRE.Guid) {

const label = MRE.Actor.Create(this.app.context, {
actor: {
name: 'label',
parentId: root.id,
exclusiveToUser: userId,
transform: { local: { position: { y: 1.5 } } },
text: {
contents: text,
height: 0.1,
anchor: MRE.TextAnchorLocation.TopCenter,
color: col
}
}
});
}

private spawnBall(root: MRE.Actor, width: number, height: number, mat: MRE.Material, userId: MRE.Guid,
force: Partial<Vector3Like>, ballRadius = 0.2, killTimeout = 5000) {
const ball = MRE.Actor.Create(this.app.context, {
actor: {
owner: userId,
parentId: root.id,
name: "ball",
appearance: {
meshId: this.assets.createSphereMesh('ball', ballRadius).id,
materialId: mat.id
},
transform: {
local: { position: { x: -width / 2 + width, y: height, z: -0.1 } }
},
rigidBody: {
mass: 3,
},
collider: { geometry: { shape: MRE.ColliderType.Auto } }
}
});

setTimeout(() => {
ball.rigidBody.addForce( force )
}, 1000);

setTimeout(() => {
// We need to disable rendering and move the ball before destroying it so that if it is currently
// colliding with a peg, it will exit collision first for the ref counting to work properly. Then
// we can destroy it after another second to process the move on the client.
ball.appearance.enabled = false;
ball.transform.app.position = new MRE.Vector3(0, -10, 0);
setTimeout(() => ball.destroy(), 1000);
}, killTimeout);
}
}
7 changes: 7 additions & 0 deletions packages/sdk/src/actor/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export interface ActorLike {
* Any actors parented to this actor will also be exclusive to the given user.
*/
exclusiveToUser: Guid;
owner: Guid;
subscriptions: SubscriptionType[];
transform: Partial<ActorTransformLike>;
appearance: Partial<AppearanceLike>;
Expand Down Expand Up @@ -99,6 +100,9 @@ export class Actor implements ActorLike, Patchable<ActorLike> {
private _name: string;
private _tag: string;
private _exclusiveToUser: Guid;

private _owner: Guid;

private _parentId = ZeroGuid;
private _subscriptions: SubscriptionType[] = [];
private _transform = new ActorTransform();
Expand Down Expand Up @@ -126,6 +130,7 @@ export class Actor implements ActorLike, Patchable<ActorLike> {

/** @inheritdoc */
public get exclusiveToUser() { return this._exclusiveToUser; }
public get owner() { return this._owner; }
public get subscriptions() { return this._subscriptions; }
public get transform() { return this._transform; }
public set transform(value) { this._transform.copy(value); }
Expand Down Expand Up @@ -722,6 +727,7 @@ export class Actor implements ActorLike, Patchable<ActorLike> {
if (from.exclusiveToUser || from.parentId) {
this._exclusiveToUser = this.parent && this.parent.exclusiveToUser || from.exclusiveToUser;
}
if (from.owner) { this._owner = from.owner; }
if (from.transform) { this._transform.copy(from.transform); }
if (from.attachment) { this.attach(from.attachment.userId, from.attachment.attachPoint); }
if (from.appearance) { this._appearance.copy(from.appearance); }
Expand All @@ -744,6 +750,7 @@ export class Actor implements ActorLike, Patchable<ActorLike> {
name: this._name,
tag: this._tag,
exclusiveToUser: this._exclusiveToUser,
owner: this._owner,
transform: this._transform.toJSON(),
appearance: this._appearance.toJSON(),
attachment: this._attachment ? this._attachment.toJSON() : undefined,
Expand Down

0 comments on commit c2809a2

Please sign in to comment.